# Run Claude Code and Codex on a headless Linux server, always on

Last updated: 2026-10-14

> **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](https://provefab.com/docs/configuration.md#repossandbox-native-execution-policy).

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](https://provefab.com/index.md) 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](https://provefab.com/docs/operations.md)).
- `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](https://provefab.com/docs/configuration.md)). Provefab's supported login modes are narrower than the standalone CLIs; see the support note above.

## Install the CLIs

Claude Code's [setup page](https://code.claude.com/docs/en/setup) gives one native installer for macOS, Linux and WSL:

```bash
curl -fsSL https://claude.ai/install.sh | bash
claude --version
```

On 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](https://learn.chatgpt.com/docs/codex/cli):

```bash
curl -fsSL https://chatgpt.com/codex/install.sh | sh
```

Run 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](https://code.claude.com/docs/en/authentication) 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](https://learn.chatgpt.com/docs/auth) recommends device code authentication, in beta, for "a remote or headless environment":

```bash
codex login --device-auth
```

Open 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:

```bash
mkdir -p ~/.config/agent-runner
( umask 077; touch ~/.config/agent-runner/env )
chmod 600 ~/.config/agent-runner/env
"${EDITOR:-nano}" ~/.config/agent-runner/env
```

Put 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](https://code.claude.com/docs/en/headless)), and Codex with `codex exec "<prompt>"` ([non-interactive mode](https://learn.chatgpt.com/docs/non-interactive-mode)). A user service does not read your shell profile, and the [systemd.exec](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html) 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`:

```ini
[Unit]
Description=Coding agent runner

[Service]
ExecStart=%h/bin/agent-runner
EnvironmentFile=%h/.config/agent-runner/env
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target
```

`%h` is your home directory ([systemd.unit](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html)). `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](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html)). `default.target` is "the main target of the user service manager." Load and start it:

```bash
systemctl --user daemon-reload
systemctl --user enable --now agent-runner
systemctl --user status agent-runner
```

## Keep 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](https://www.freedesktop.org/software/systemd/man/latest/loginctl.html), "a user manager is spawned for the user at boot and kept around after logouts." Turn it on once, with administrator rights:

```bash
sudo loginctl enable-linger "$USER"
loginctl show-user "$USER" --property=Linger
```

Without 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:

```bash
journalctl --user -u agent-runner        # everything
journalctl --user -u agent-runner -f     # follow live
```

`--user` shows "messages from service of current user", and `-f` keeps printing new entries ([journalctl](https://www.freedesktop.org/software/systemd/man/latest/journalctl.html)). 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`:

```bash
curl -fsSL https://provefab.com/install.sh | sh
```

A static `.tar.gz` for x86_64 or ARM64 is also on the GitHub Releases page. On Linux, HTTPS needs the `ca-certificates` package. Then:

```bash
provefab login claude        # shows a link, takes the code you paste
provefab login codex         # shows a device code to enter on another machine
provefab init
provefab repos add your-account/your-repo
# Provision SRT and offline dependencies; declare protected validation paths.
# Confirm runtime/login support before starting tasks.
provefab doctor
provefab run --dry-run
provefab service install --workers 1
```

Both 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](https://provefab.com/docs/configuration.md)).

`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](https://provefab.com/docs/operations.md)).

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](https://provefab.com/docs/agents.md); it leaves the sign-ins, keys and service to you. What the pull request carries is in [How to verify AI-generated pull requests](https://provefab.com/guides/verify-ai-pull-requests.md). To start work from Jira Cloud or Linear instead of GitHub issues, see [Jira or Linear ticket to pull request](https://provefab.com/guides/jira-linear-ticket-to-pull-request.md).

## 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](https://provefab.com/docs/security.md)).
- 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 `sudo` for you: lingering is a command you run.
- Consumer plans can restrict commercial use; check your terms.
