fdbck.

Run the worker 24/7

The worker only picks up feedback while it's running. For a real deployment you want it to start on boot, restart if it crashes, and write logs somewhere you can read them. Pick the recipe that matches where you're running it. Each one is copy-paste; swap the paths for yours.

New here? Start with Run the worker to download, configure, and test it once — then come back to make it permanent.

First: the auth caveat

Whatever you run the worker under — a service, a container, a VM — it must inherit a machine that is already authenticated with your agent. For Claude that means you ran claude setup-token as the same user the service runs as (interactive claude /login does not persist a token a headless service can use). The worker probes this at startup and fails fast if it can't reach your agent, so if a service “starts but does nothing,” check its logs for an auth error first. See Claude auth: subscription or API key.

macOS — always-on desktop (launchd)

For a Mac that stays on, a LaunchAgent is the native way to start the worker at login and keep it alive. Write ~/Library/LaunchAgents/app.fdbck.worker.plist:

~/Library/LaunchAgents/app.fdbck.worker.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>            <string>app.fdbck.worker</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/node</string>
    <string>/Users/you/fdbck/fdbck-worker.mjs</string>
  </array>
  <key>WorkingDirectory</key> <string>/Users/you/fdbck</string>
  <key>RunAtLoad</key>        <true/>
  <key>KeepAlive</key>        <true/>
  <key>StandardOutPath</key>  <string>/Users/you/fdbck/worker.log</string>
  <key>StandardErrorPath</key><string>/Users/you/fdbck/worker.err.log</string>
  <!-- so 'node' resolves and setup-token is found -->
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key>
    <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
  </dict>
</dict>
</plist>

Load it (and reload after edits):

shell
launchctl unload ~/Library/LaunchAgents/app.fdbck.worker.plist 2>/dev/null
launchctl load  ~/Library/LaunchAgents/app.fdbck.worker.plist
tail -f ~/fdbck/worker.log

Find your Node path with which node. A LaunchAgent runs only while you're logged in — if the Mac reboots to the login screen and nobody logs in, it won't start. For a truly headless Mac, enable automatic login, or use a LaunchDaemon in /Library/LaunchDaemons instead.

Linux — systemd (the default choice)

On any modern Linux box — a spare machine, a VPS, a VM — a systemd unit gives you restart-on-crash, start-on-boot, and journalctl logs for free. A user unit is simplest because it inherits your agent auth.

User unit

~/.config/systemd/user/fdbck-worker.service
[Unit]
Description=fdbck worker
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/node /home/you/fdbck/fdbck-worker.mjs
WorkingDirectory=/home/you/fdbck
Restart=always
RestartSec=5
# cap memory so a runaway run can't take the box down
MemoryMax=2G

[Install]
WantedBy=default.target
shell
systemctl --user daemon-reload
systemctl --user enable --now fdbck-worker
loginctl enable-linger "$USER"        # keep running after you log out
journalctl --user -u fdbck-worker -f  # follow the logs

enable-linger is the important line — without it the user unit stops when your login session ends.

System unit (dedicated user)

Prefer a system service? Run it as a dedicated user that has been through claude setup-token. Write /etc/systemd/system/fdbck-worker.service:

/etc/systemd/system/fdbck-worker.service
[Unit]
Description=fdbck worker
After=network-online.target
Wants=network-online.target

[Service]
User=fdbck
Group=fdbck
ExecStart=/usr/bin/node /opt/fdbck/fdbck-worker.mjs
WorkingDirectory=/opt/fdbck
Restart=always
RestartSec=5
MemoryMax=2G

[Install]
WantedBy=multi-user.target
shell
sudo systemctl daemon-reload
sudo systemctl enable --now fdbck-worker
sudo journalctl -u fdbck-worker -f

Docker / Compose

A container is a clean way to pin Node and get restart-on-crash. The one wrinkle is auth: the container needs your agent credentials, so mount them in (don't bake them into an image). A restart: unless-stopped policy keeps it up across reboots and crashes.

docker-compose.yml
services:
  fdbck-worker:
    image: node:22-slim
    working_dir: /app
    command: node fdbck-worker.mjs
    restart: unless-stopped
    mem_limit: 2g
    environment:
      # Subscription auth: run `claude setup-token` and export this in the
      # shell that runs compose. (API-key mode: use ANTHROPIC_API_KEY instead.)
      - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN}
    volumes:
      - ./fdbck-worker.mjs:/app/fdbck-worker.mjs:ro
      - ./worker.json:/root/.fdbck/worker.json:ro   # worker reads ~/.fdbck/worker.json
      - ./repo:/app/repo                 # a checkout the worker can push from
      - ~/.gitconfig:/root/.gitconfig:ro
shell
docker compose up -d
docker compose logs -f fdbck-worker

Auth goes in as an environment variable, not a mounted home directory. For subscription mode, run claude setup-token (an interactive claude /login does not persist a headless-usable token) and export CLAUDE_CODE_OAUTH_TOKEN in the shell that runs docker compose up. For API-key mode, pass ANTHROPIC_API_KEY the same way. The worker's config file is mounted to ~/.fdbck/worker.json — the only path it reads it from.

Home lab — Proxmox LXC or VM

On Proxmox (or any hypervisor), the worker is happiest in a small LXC container or VM that's always on. There's nothing fdbck-specific about the virtualization — inside the guest you install Node, authenticate your agent, and use the systemd recipe above.

A minimal Debian/Ubuntu LXC, from scratch:

shell
# inside the container, as your user
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs git
git clone https://github.com/you/your-repo.git ~/fdbck/repo
curl -fsSL https://fdbck.app/fdbck-worker.mjs -o ~/fdbck/fdbck-worker.mjs
# authenticate your agent as THIS user, then use the systemd user unit above
claude setup-token

Give the LXC/VM enough RAM for a build to run (2–4 GB is comfortable) and set it to start on boot in the Proxmox options. An unprivileged LXC is fine — the worker needs no special capabilities.

Cloud VPS (DigitalOcean, Hetzner, EC2, Fly…)

A $5 VPS is plenty. Provision a small Linux box, install Node + git, clone the repo the worker will push from, authenticate your agent as the user the service runs as, then use the systemd recipe above — that's the durable answer on a VPS.

Two lighter options when you just want it up quickly:

tmux / screen — quick and dirty

Good for a first run or a throwaway box; it survives an SSH disconnect but not a reboot, so graduate to systemd once it's working.

shell
tmux new -s fdbck                 # start a session
node fdbck-worker.mjs             # run the worker inside it
# detach with Ctrl-b then d; reattach later with:
tmux attach -t fdbck

PM2 — process manager

If you already run Node apps with PM2, it handles restart, logs, and a boot hook without writing a unit file:

shell
npm i -g pm2
pm2 start fdbck-worker.mjs --name fdbck-worker --max-memory-restart 2G
pm2 logs fdbck-worker
pm2 startup      # prints a command to run so PM2 restarts on boot
pm2 save         # remember the current process list

Windows — Task Scheduler or NSSM

On an always-on Windows machine, register the worker so it starts at logon and restarts if it exits.

Task Scheduler (built in)

PowerShell (run once)
$action  = New-ScheduledTaskAction -Execute "node.exe" `
  -Argument "C:\fdbck\fdbck-worker.mjs" -WorkingDirectory "C:\fdbck"
$trigger = New-ScheduledTaskTrigger -AtLogOn
$settings = New-ScheduledTaskSettingsSet `
  -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1)
Register-ScheduledTask -TaskName "fdbck-worker" `
  -Action $action -Trigger $trigger -Settings $settings

NSSM — run as a real service

NSSM wraps the worker as a Windows service (starts before logon, auto-restarts):

shell
nssm install fdbck-worker "C:\Program Files\nodejs\node.exe" "C:\fdbck\fdbck-worker.mjs"
nssm set fdbck-worker AppDirectory "C:\fdbck"
nssm set fdbck-worker AppStdout "C:\fdbck\worker.log"
nssm set fdbck-worker AppStderr "C:\fdbck\worker.err.log"
nssm start fdbck-worker

Don't want a server at all?

You can skip the always-on machine entirely and run the worker on a schedule in CI — a GitHub Actions cron that drains the queue every few minutes with an API key. That path (and its trade-offs) is covered in Run it in CI on the main worker page.

Whatever you pick, confirm

  • Auto-restart — kill the process and watch it come back (Restart=always, KeepAlive, unless-stopped, PM2).
  • Starts on boot — reboot the machine and check the worker is back without you touching it.
  • Logs — you can read what it's doing (journalctl, docker compose logs, the log files above).
  • Auth survives a restart — the service user stays authenticated with your agent after a reboot.
  • Memory cap — a ceiling (2 GB is a good default) so one heavy run can't take the box down.

The dashboard shows a live worker status pill, so once it's running you can confirm from anywhere that fdbck can see it.