Coord Docs
Runner

Headless mode

Run the Coord Runner on a server with no display, controlled entirely from the command line.

Headless mode runs the Coord Runner on a machine with no display — a cloud server, an ARM board, or any host you reach over SSH. You control it from the command line (the text-based terminal app on your computer) instead of the desktop window, and it picks up and runs agentic jobs exactly as the desktop app does.

This is primarily for Linux servers that have no desktop environment, including GPU hosts and ARM64 boards (NVIDIA DGX Spark and Jetson, AWS Graviton, Ampere). The same command-line controls also work on macOS and Windows from a terminal, which is convenient over SSH — though on those platforms you can also just use the desktop app.

Note: Everything you configure in the desktop app — runner name, runner workspace directory, max concurrent jobs, agent selection — lives in the same settings file and applies identically in headless mode. Headless mode changes how the runner is launched and controlled, not how it works.

Install on Linux

  1. Download the AppImage for your architecture from the Runners page in Coord, or transfer it to the server:
    • x64 (Intel/AMD): Coord-Runner-linux-x64.AppImage
    • arm64 (DGX Spark, Jetson, Graviton, Ampere): Coord-Runner-linux-arm64.AppImage
  2. Make it executable:
    chmod +x Coord-Runner-linux-arm64.AppImage
  3. (Optional) Symlink it to a short name on your PATH so the examples below read cleanly:
    sudo ln -s "$(pwd)/Coord-Runner-linux-arm64.AppImage" /usr/local/bin/coord-runner

Note: AppImages normally need FUSE to run. Many minimal server images don't include it. If you see a FUSE error, either install FUSE (sudo apt install libfuse2) or run the AppImage with --appimage-extract-and-run, which unpacks and runs it without FUSE:

./Coord-Runner-linux-arm64.AppImage --appimage-extract-and-run login

The rest of this page uses coord-runner as the command. Substitute the AppImage filename (and --appimage-extract-and-run if needed) if you didn't create a symlink.

Sign in

Connect the runner to your Coord account with the device code flow:

coord-runner login

The runner prints a URL and a short code:

To authenticate, open this URL in a browser (any machine works):
    https://app.coord.io/auth/device?user_code=ABCD-1234
and enter the code: ABCD-1234

Waiting for authorization…

Open the URL on any device — your laptop, your phone — and confirm the code. The server itself never needs a browser. Once you approve, the runner stores your credentials and is ready to run jobs.

Configure

Settings live in a JSON file on the server:

PlatformLocation
Linux~/.config/coord-runner/settings.json
macOS~/Library/Application Support/coord-runner/settings.json
Windows%APPDATA%\coord-runner\settings.json

The keys match the desktop app's settings: the runner's display name, the runner workspace directory where repositories are cloned, max concurrent jobs, agent selection, and repository cleanup timing. See Configuration for what each setting does.

On its first headless run, the runner registers itself automatically using the machine's hostname (or the name in your settings file) and the agents it detects on the server.

Warning: Agentic jobs run real AI coding agents (Claude Code, Codex, Gemini CLI), so those tools must be installed on the server and reachable on the runner's PATH. The runner prints a summary of which agents it found at startup. If you run under a service manager, set PATH explicitly in the service definition — see the systemd example below.

Run

Start the runner and have it pick up jobs:

coord-runner --headless

This honors your Start runner on open setting. To force the runner to start picking up jobs regardless of that setting:

coord-runner --headless start

At startup the runner prints a summary — the connected account, runner name, workspace directory, max concurrent jobs, and detected agents — so you can confirm it's configured correctly without a window. It then logs each job as it claims and runs it.

To leave it running after you disconnect, run it under a service manager (recommended — see below) or background it with your shell's job control.

Stopping the runner

The runner shuts down cleanly on any of these signals. A clean shutdown releases the jobs it's currently running so they immediately become available for another runner to pick up, instead of waiting to be reclaimed.

SignalHow it's sentBehavior
SIGINTCtrl-C in the terminalReleases in-progress jobs, then exits
SIGTERMA service manager stop (systemd, launchd), or kill <pid>Releases in-progress jobs, then exits
SIGHUPThe terminal closes or an SSH session dropsReleases in-progress jobs, then exits

The SIGHUP case matters for SSH-driven operation: if you start the runner over SSH and your connection drops (or you close the terminal), the runner still shuts down cleanly rather than leaving its jobs stranded.

Warning: A forced kill — kill -9 (SIGKILL), an out-of-memory kill, or power loss — cannot be intercepted, so the runner can't release its jobs on the way out. Coord automatically reclaims those jobs after a short timeout (about 30–60 seconds), so they aren't lost — but they're held for that window. Prefer a clean stop whenever possible. Running under a service manager handles this for you: systemd's default stop signal is SIGTERM, so systemctl stop always drains cleanly.

Run as a service (systemd)

Running the Coord Runner as a systemd service keeps it alive across reboots and gives you clean start/stop and log capture. Create /etc/systemd/system/coord-runner.service:

[Unit]
Description=Coord Runner
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
# The user the runner runs as. This user owns the credentials and settings.
User=coord
# Agents (Claude Code, Codex, Gemini) and tools must be on PATH. systemd does
# NOT inherit your login shell's PATH — set it explicitly to include wherever
# your agent CLIs and Node.js live.
Environment=PATH=/usr/local/bin:/usr/bin:/bin:/home/coord/.local/bin:/home/coord/.nvm/versions/node/current/bin
ExecStart=/usr/local/bin/coord-runner --headless start
Restart=on-failure
RestartSec=5
# Give the runner time to release in-progress jobs before being killed.
TimeoutStopSec=90

[Install]
WantedBy=multi-user.target

Then enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now coord-runner

Sign in once before (or just after) starting the service, as the same user the service runs as:

sudo -u coord coord-runner login

Note: systemd stops services with SIGTERM, which the runner drains cleanly. TimeoutStopSec=90 gives in-progress jobs time to wind down before systemd escalates to a forced kill.

macOS and Windows

For unattended operation on macOS or Windows, wrap the runner with the platform's service manager — launchd (macOS) or a Windows Service / NSSM (Windows) — pointing at coord-runner --headless start. Use --headless here: plain coord-runner start opens the desktop window, which won't work under a service manager with no display. The same graceful-shutdown behavior applies. These platforms aren't a primary target for headless operation; if you hit a rough edge, the desktop app is the supported path.

Updating

Check whether a newer version is available without changing anything:

coord-runner update --check

This exits 0 if you're on the latest version and 1 if an update is available, so you can use it in scripts. Any other problem (for example, the update server is unreachable) exits with a different non-zero code, so a failed check isn't mistaken for an available update.

Apply an available update:

coord-runner update

The runner downloads the new version, verifies its signature, and replaces itself in place. It does not restart automatically. If you run under a service manager, restart it afterward to load the new version:

sudo systemctl restart coord-runner

Warning: Stop any running instance before updating to avoid file-replacement conflicts. The command prints a reminder.

Command-line updates apply on Linux. On macOS and Windows, update from the desktop app instead — it checks automatically and installs in place.

Logs

In headless mode the runner writes logs to standard output. Under systemd, view them with:

journalctl -u coord-runner -f

Logs default to an informational level. For more detail when diagnosing an issue, set the RUST_LOG environment variable (for example RUST_LOG=debug) before launching, or add it to the Environment= lines in the service definition.

Approving agent tools

When an agent needs approval to run a tool, you approve it from the Coord web app at app.coord.io — not on the server. This is the same approval flow the desktop app uses, so a runner with no window is fully supported.

For unattended servers where no one is watching for approval prompts, set jobs to auto-approve tools when you create them. Coord's safety limits still apply to dangerous operations.

Notes and limitations

  • Minimal Linux images and credential storage. The runner stores credentials in the system keyring, which on Linux needs a Secret Service provider (such as GNOME Keyring) reachable over D-Bus. A bare server without a desktop session may not have one. If sign-in fails to persist, install a Secret Service provider, or reach out to support — a file-based credential option is on the roadmap.
  • PATH under a service manager. Service managers don't inherit your interactive shell's PATH. If the runner reports that an agent isn't detected even though it's installed, set PATH explicitly in the service definition (see the systemd example).

Next steps

On this page