Run Claude Code and Codex on a headless Linux server, always on
Support update, 14 October 2026: Native enforcement and login modes are restricted. Linux enforcement remains unverified; all Claude stages temporarily refuse. Select Codex or Pi. See current versions, prerequisites and protection limits.
Install both CLIs as your own user, then sign in with the flows made for machines without a browser: Claude Code gives you a link to open elsewhere and takes the code you paste back, and Codex shows a device code. Keep any API key in an environment file with mode 600, run your agent process as a systemd user service, and turn on lingering so it keeps running after you log out. Provefab does the same with one install line and a few commands, and adds what to run: labelled issues that become pull requests.
What you need
- A Linux server you can reach over SSH. For Provefab, it must be x86_64 or ARM64.
- Your own user account. The agents run as that account, not as root; you need administrator rights once, for lingering.
- systemd, which runs the service. Provefab’s service file needs systemd 240 or later (Operations).
git, and the repositories the agents will work in.- A Claude or ChatGPT plan, or API keys. Check your plan’s terms before you use it for work: consumer plans can restrict commercial use. Anthropic’s consumer terms for EEA and Swiss residents, for example, say “Non-commercial use only” (Configuration). Provefab’s supported login modes are narrower than the standalone CLIs; see the support note above.
Install the CLIs
Claude Code’s setup page gives one native installer for macOS, Linux and WSL:
curl -fsSL https://claude.ai/install.sh | bashclaude --versionOn Linux it installs the launcher at ~/.local/bin/claude, under your home directory. The same page offers apt, dnf and apk repositories (those need sudo) and an npm package, with a plain warning: “Do NOT use sudo npm install -g”.
Codex has a standalone installer for macOS and Linux on its CLI page:
curl -fsSL https://chatgpt.com/codex/install.sh | shRun both as the account the agents will use, so their files and logins belong to it.
Sign in without a browser
Claude Code. Run claude. The authentication page covers the headless case: if the browser does not open, press c to copy the login URL and open it on another machine. When the browser shows a login code instead of redirecting back, “paste it into the terminal at the Paste code here if prompted prompt”, which the page says “is common in WSL2, SSH sessions, and containers.” For scripts, claude setup-token prints a one-year token to set as CLAUDE_CODE_OAUTH_TOKEN; the page recommends it “for CI pipelines and scripts where browser login isn’t available.” With an API key instead, set ANTHROPIC_API_KEY.
Codex. The Codex authentication page recommends device code authentication, in beta, for “a remote or headless environment”:
codex login --device-authOpen the link on any machine with a browser, sign in, and enter the one-time code. Device code login may need enabling first, in your ChatGPT security settings for a personal account or in the workspace permissions for a workspace admin. For an API key, pipe it in: printenv OPENAI_API_KEY | codex login --with-api-key. Codex then keeps the login “in a plaintext file at ~/.codex/auth.json or in your OS-specific credential store”.
Keep keys in a file only you can read
The service reads its secrets from an environment file. Create it with a mode that leaves group and others out:
mkdir -p ~/.config/agent-runner( umask 077; touch ~/.config/agent-runner/env )chmod 600 ~/.config/agent-runner/env"${EDITOR:-nano}" ~/.config/agent-runner/envPut one NAME=value per line, for example ANTHROPIC_API_KEY=... or CLAUDE_CODE_OAUTH_TOKEN=.... Be clear about what this protects. The file is protected by its permissions, not encrypted: anyone with root, and any process running as your account, can read it. That includes the agents themselves.
Run it as a systemd user service
Neither CLI watches a queue by itself, so the service runs a script you write, here ~/bin/agent-runner: it decides what to work on and calls the CLIs non-interactively. Claude Code runs a single prompt with claude -p "<prompt>" (headless mode), and Codex with codex exec "<prompt>" (non-interactive mode). A user service does not read your shell profile, and the systemd.exec page says the user manager’s PATH “may be configured by the distribution”, so call the CLIs by full path or set PATH in the environment file.
Write the unit to ~/.config/systemd/user/agent-runner.service:
[Unit]Description=Coding agent runner
[Service]ExecStart=%h/bin/agent-runnerEnvironmentFile=%h/.config/agent-runner/envRestart=on-failureRestartSec=30
[Install]WantedBy=default.target%h is your home directory (systemd.unit). EnvironmentFile= reads “newline-separated variable assignments” from the file. Restart=on-failure restarts the script when it “exits with a non-zero exit code” or is killed by a signal, and RestartSec=30 waits 30 seconds first (systemd.service). default.target is “the main target of the user service manager.” Load and start it:
systemctl --user daemon-reloadsystemctl --user enable --now agent-runnersystemctl --user status agent-runnerKeep it running after you log out
A user service belongs to your user manager, which normally stops when your last session ends. Lingering changes that: according to loginctl, “a user manager is spawned for the user at boot and kept around after logouts.” Turn it on once, with administrator rights:
sudo loginctl enable-linger "$USER"loginctl show-user "$USER" --property=LingerWithout it, the service stops when you close your SSH session and does not start at boot.
Read the logs
systemd keeps what your script prints to stdout and stderr in the journal:
journalctl --user -u agent-runner # everythingjournalctl --user -u agent-runner -f # follow live--user shows “messages from service of current user”, and -f keeps printing new entries (journalctl). Logs your script writes to its own files stay there.
One common trap: after su - <user>, systemctl --user fails with Failed to connect to bus, because that shell has no user systemd session. Log in over SSH as that user instead, or set XDG_RUNTIME_DIR=/run/user/$(id -u).
The same setup with Provefab
Provefab packages these steps, and adds the part you would otherwise write in agent-runner. Install it into ~/.local/bin, without sudo:
curl -fsSL https://provefab.com/install.sh | shA static .tar.gz for x86_64 or ARM64 is also on the GitHub Releases page. On Linux, HTTPS needs the ca-certificates package. Then:
provefab login claude # shows a link, takes the code you pasteprovefab login codex # shows a device code to enter on another machineprovefab initprovefab repos add your-account/your-repo# Provision SRT and offline dependencies; declare protected validation paths.# Confirm runtime/login support before starting tasks.provefab doctorprovefab run --dry-runprovefab service install --workers 1Both logins live in Provefab’s own config directories (~/.provefab/claude, ~/.provefab/codex), kept apart from your own sessions. Codex also supports --api-key, using ~/.provefab/codex-api. Claude API-key profiles can be configured in ~/.provefab/claude-api, but native stages refuse them. On Linux, the Anthropic key, the Jev key and the Jira and Linear credentials go to ~/.provefab/credentials.toml, created with mode 600 and refused when group or others can read it. Like your own env file, it is protected by its permissions, not encrypted (Configuration).
provefab service install writes ~/.config/systemd/user/provefab.service, then runs systemctl --user daemon-reload, enable and restart. When lingering is off, it prints sudo loginctl enable-linger <user> for you to run: Provefab never runs sudo itself, and provefab doctor shows a lingering line. Logs go to ~/.provefab/logs/run.log; service install rotates it to run.log.1 once it passes 10 MB, and journalctl --user -u provefab shows systemd’s own messages. The service keeps the PATH of the terminal you install it from, so run service install again after you install or move a tool (Operations).
On top of the service, Provefab decides what to run: it picks up GitHub issues you label provefab (Jira Cloud and Linear in beta), filters every agent tool call through a guard, runs your repository’s checks, has a model from another provider review the change when your catalog has a model from another provider, and opens a pull request that waits for a person. To let a coding agent do the configuration, point it at Set up with a coding agent; it leaves the sign-ins, keys and service to you. What the pull request carries is in How to verify AI-generated pull requests. To start work from Jira Cloud or Linear instead of GitHub issues, see Jira or Linear ticket to pull request.
Limits
- Agents run as your account with native command restrictions plus a guard. Protection uses explicit secret and validation path lists; it does not isolate every host resource. Missing enforcement refuses execution (Security).
- Keys on Linux are not encrypted at rest, with or without Provefab.
- Provefab opens pull requests on GitHub only, and runs on Linux x86_64 and ARM64 (and macOS), not on other architectures.
- It does not manage
sudofor you: lingering is a command you run. - Consumer plans can restrict commercial use; check your terms.