Skip to content

Install a runner

One command installs a runner, and it is the same command on Linux and macOS.

Terminal window
curl -fsSL https://github.com/commandchain-ai/commandchain-runner-dist/releases/latest/download/install.sh | sh

That installs the runner and then runs it right there in the terminal, on the runner screen, which shows the pairing code, what the runner found on the machine, and anything left to do. Nothing is installed as a background service, so the runner stops when you press Ctrl-C or close the window, and commandchain-runner starts it again.

This is also the only way to run a runner on a machine with no user service manager, such as a container or WSL without systemd.

Terminal window
curl -fsSL https://github.com/commandchain-ai/commandchain-runner-dist/releases/latest/download/install.sh | sh -s -- --service

--service installs the same runner as a background service that starts with the machine and keeps working after you close the terminal. It is what a machine doing real work ends up on. Run it whenever you are ready, including on a machine where you already ran the command above.

Either command pairs the machine with the hosted CommandChain. If you run CommandChain yourself, add --server https://your-commandchain.

Opening Settings, then Runners, then Add runner in the app always shows the exact command for the workspace you are looking at.

The installer needs no administrator rights and no runtime. There is no Node, no npm, no Docker, no build step, no GitHub account, and no token to paste. Every asset it fetches, the installer itself, the four executables, and their SHA256SUMS, comes from a public release mirror that accepts no credential.

Terminal window
--server <url> The instance to pair with. Default: the hosted
CommandChain API. Pass it only if you run
CommandChain yourself.
--display-name <name> How this machine appears in CommandChain. Default: user@host.
--version <tag> Release to install, for example v1.2.0. Default: latest.
--prefix <dir> Where the program goes. Default: $HOME/.local/bin.
--status Show what is installed, where the logs are, and whether it runs.
--uninstall Stop and remove the service and the program.
--purge With --uninstall: also delete pairing credentials and workspaces.
--skip-agent-login Do not sign the runner in to a coding agent during install.
The runner installs and runs either way. To sign it in
later, open it with "commandchain-runner" and follow its
prompt, or run "commandchain-runner signin --agent claude-code".
--service Install a background service that starts the runner with
the machine and keeps it working after you close the
terminal. Without it, the default, the runner is
installed and then run in this terminal, where it pairs
on its own screen and stops when you close the window.
The default is also the only mode that works on a
machine with no user service manager.
--help This text.

The installer prints each step as it does it. Here is a real run, on a machine that had nothing installed:

Terminal window
$ curl -fsSL https://github.com/commandchain-ai/commandchain-runner-dist/releases/latest/download/install.sh \
| sh -s -- --server https://your-commandchain --display-name maya-macbook
CommandChain self-hosted runner (installer 0.1.2)
machine linux-x64
server https://your-commandchain
prefix /home/maya/.local/bin
1. Downloading the runner (linux-x64) from https://github.com/commandchain-ai/commandchain-runner-dist/releases/latest/download
✔ downloaded commandchain-runner-linux-x64 and SHA256SUMS
2. Verifying the checksum before anything is executed
✔ sha256 1a5389db3a465dd069489aa4dd3833b46beabf185dd88d49d79999d6d1747ab9 matches SHA256SUMS
3. Installing to /home/maya/.local/bin/commandchain-runner
✔ installed commandchain-runner 0.1.2
4. Installing the Claude Code CLI (native build - no Node required)
✔ claude at /home/maya/.local/bin/claude (2.1.218 (Claude Code))
5. Checking that this machine can contain a coding agent
commandchain-runner doctor - containment check
host type self_hosted
provider linux-landlock
platform linux
tier landlock
OK - every agent process on this machine will run inside this boundary.
✔ containment established (see the report above)
6. Pairing with CommandChain (https://your-commandchain)
────────────────────────────────────────────────────────────
Pair this runner to approve it for a tenant:
Code: 329X-4QKT
URL: https://app.your-commandchain/app/pair?code=329X-4QKT
Waiting for approval (up to 10 min)...
────────────────────────────────────────────────────────────
✔ Paired with CommandChain.
instance https://your-commandchain
tenant alpenglow
runnerId 32c152e4-3d5e-49a1-b3df-20b95c0a74b6
displayName maya-macbook
credential /home/maya/.commandchain/runner.json (0600, never printed)
7. Coding agent sign-in for this runner
Signing this runner in to Claude Code. Your browser opens; this sign-in belongs to the runner and never leaves this machine.
✔ The runner is signed in to Claude Code. Nothing is uploaded.
8. Installing the user service
✔ wrote /home/maya/.config/systemd/user/commandchain-runner.service
✔ commandchain-runner is running and will restart itself if it stops
Done
✔ the runner is installed, paired, contained, and running as a user service
open: commandchain-runner (the runner screen: status, sign-in, and anything left to do)
status: sh install.sh --status
logs: /home/maya/.local/state/commandchain-runner/runner.log
uninstall: sh install.sh --uninstall

Step by step:

  1. Download. One executable, chosen for your operating system and CPU, plus the release’s SHA256SUMS.

  2. Verify. The checksum is checked before anything is executed. A mismatch stops the install.

  3. Install the program. Into ~/.local/bin by default, or wherever --prefix says.

  4. Install the coding agent. Claude Code’s own native build, which also needs no Node.

  5. Prove containment. The installer runs doctor, which builds a real sandbox policy and runs a real process through it. If this machine cannot contain an agent, the install stops here and points you at a managed runner. There is no flag that turns this into a warning.

  6. Pair. A device code, approved in your browser. See pairing.

  7. Coding agent sign-in for this runner. With a terminal, Claude Code’s own browser sign-in runs for the runner, into ~/.commandchain/agent-auth. Nothing is uploaded, and your own sign-in is not touched. With no terminal, or with --skip-agent-login, open the runner later with commandchain-runner and follow its prompt. See sign-in only or full setup.

  8. Install the service. A user-level service, started immediately, restarted if it stops.

Nothing is installed outside your home directory, and nothing asks for sudo.

WhatWhere
The program~/.local/bin/commandchain-runner
Pairing credential, workspaces, the runner’s own sign-ins~/.commandchain (mode 0700). COMMANDCHAIN_RUNNER_STATE_DIR moves all of them
The service~/.config/systemd/user/ on Linux, ~/Library/LaunchAgents/ on macOS
Logs~/.local/state/commandchain-runner/ on Linux, ~/Library/Logs/commandchain-runner/ on macOS

The pairing credential is written to a 0600 file. It is never printed, never a command-line argument, and never written into the service definition.

A runner installed the default way stopped when you closed that terminal. Start it again with one command, from a terminal on that machine:

Terminal window
commandchain-runner

That is also how you open the runner screen for a runner that is already running in the background: it attaches to the running one rather than starting a second, shows its status and anything left to do, and closing it leaves the background service running.

A background-service install starts itself with the machine, so usually there is nothing to do. When the app says that runner is offline or stale, start the service from a terminal on that machine.

On Linux:

Terminal window
systemctl --user reset-failed commandchain-runner 2>/dev/null; systemctl --user restart commandchain-runner

reset-failed matters. A runner holding a credential the workspace no longer accepts exits immediately, so systemd stops retrying it and parks the unit in a failed state, where a plain restart answers Start request repeated too quickly and does nothing. On a healthy unit it does nothing at all.

On macOS:

Terminal window
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.commandchain.runner.plist 2>/dev/null \
|| launchctl kickstart -k gui/$(id -u)/ai.commandchain.runner

Both commands work whether the service is stopped or already running: they load it if it is unloaded and restart it if it is not. The runner comes back online in the app within a few seconds. If it does not, read the log, because a runner that starts and exits says why in its first lines:

Terminal window
tail -f "${XDG_STATE_HOME:-$HOME/.local/state}/commandchain-runner/runner.log" # Linux
tail -f ~/Library/Logs/commandchain-runner/runner.log # macOS

Running the install command again is safe too. It reuses the pairing and credentials this machine already has, so it does not create a second runner. For the same reason it cannot fix a credential the workspace has stopped accepting: open the runner with commandchain-runner and press enter to pair again (or run commandchain-runner connect --force-pair --pair-only), then the start command above once more. See troubleshooting.

To stop it, use systemctl --user stop commandchain-runner on Linux or launchctl bootout gui/$(id -u)/ai.commandchain.runner on macOS. Both are temporary: the service comes back at the next login. To remove it for good, see upgrades and uninstall.

On Linux the service runs while you are logged in. To keep it running afterwards, enable lingering for your own user:

Terminal window
loginctl enable-linger "$USER"