---
title: "fredrin-agent - setup and pairing guide"
description: "Install fredrin-agent, pair a machine with your Fredrin workspace, and keep it running: prerequisites, the install one-liner, and troubleshooting."
url: "https://fredrin.com/docs/remote-agent"
updated: "2026-09-01"
---

# fredrin-agent - setup and pairing guide

`fredrin-agent` is the standalone daemon that runs Fredrin tickets on your own
always-on machine (a home server, a cloud VM, a spare Mac). It dials out to
Fredrin over Ably — no inbound ports, no VPN — receives dispatched tickets, runs
your coding CLI in an isolated per-ticket worktree, and streams the live
terminal back to the ticket UI.

You never need Fredrin's source code: the agent ships as a single
self-contained bundle downloaded by the installer.

## Prerequisites

On the machine that will run jobs:

- **Node.js 20+** (includes `npm`, which the installer uses to fetch a prebuilt
  terminal engine — no compiler or root required on common platforms).
- **GitHub CLI** (`gh`) **installed** — Fredrin uses it to open PRs when Workers
  ship. It does **not** need to be signed in, and signing it in is not a setup
  step: Fredrin supplies the GitHub credential with every job. Signing it in
  changes which GitHub account this machine's jobs run as — read
  [Which GitHub account jobs run as](#which-github-account-jobs-run-as) first.
- **A supported coding CLI**, installed. For **Claude Code** a machine with its
  own sign-in just uses it (the local login always wins); a machine without one
  — headless boxes and containers included — is covered by the workspace's
  **subscription token** (minted once with `claude setup-token`, stored in
  Settings → Models), delivered per-job. The other CLIs still need their own
  sign-in on the machine:
  - Cursor Agent — `agent login` (default)
  - Claude Code — `claude /login`, or no sign-in when the workspace has a
    subscription token
  - Codex CLI — `codex login` (with ChatGPT)
  - GitHub Copilot CLI — `npm i -g @github/copilot`
- **A clone of your project repo** on the machine — jobs run in worktrees
  created from this checkout. Note its path; you'll enter it during pairing.
- `curl` or `wget` (macOS/Linux). On Windows PowerShell's own `irm` does the
  job, so there is nothing to install.

**On Windows the installer supplies most of that itself.** `install.ps1`
installs Node.js 20+ and `gh` for you (via `winget`, falling back to `choco`)
when they are missing, and leaves `gh` signed out — which is what you want, see
[Which GitHub account jobs run as](#which-github-account-jobs-run-as). What it
does **not** install is **Git for Windows**, and that one is not optional: every
job runs inside a `bash -l` login shell, so a machine without it pairs, reports
healthy, and then fails every ticket at spawn. See
[Windows](#windows) below.

Run the agent as a **non-root user** where the machine has one: Claude Code
refuses `--dangerously-skip-permissions` — the flag every unattended Worker runs
on — under root. On a box whose *only* account is root (a microVM, a container, a
cloud image at first boot) that is not a choice, so the setup prompt below
resolves it instead of warning about it: it declares the sandbox
(`IS_SANDBOX=1`), and `fredrin-agent service install` writes the same declaration
into the supervisor unit whenever the daemon will run as root. See
[Running as root](#running-as-root).

### Which GitHub account jobs run as

This is the one rule to know before you set a machine up, and it is written here
only — everything else on this page, and the setup prompt below, links back to
it.

**Fredrin sends a GitHub credential with every job** — the credential of whoever
dispatched the ticket. So an agent machine needs no GitHub secret of its own, and
a fleet box, a container or a cloud image can hold none at all.

**A `gh` login on the machine outranks it.** If `gh auth login` has been run here,
every job this machine runs acts as *that* GitHub account, no matter who
dispatched it. When that account can see every repo behind the workspace's
projects, nothing goes wrong and you may never notice. When it cannot, GitHub
answers a repo it is not allowed to see with *not found* rather than *forbidden*,
so the tickets for that project all die at clone —

```text
git clone https://github.com/<owner>/<repo> failed:
GraphQL: Could not resolve to a Repository with the name '<owner>/<repo>'
```

— naming a repository that exists and that the dispatching user can see
perfectly well. The machine stays green in **Settings → Remote agents** the whole
time, because the wrong identity is only reached once a job starts.

So: **install `gh` and leave it signed out**, unless you deliberately want this
machine pinned to one GitHub account. If it is already signed in and jobs are
failing at clone, `gh auth logout` and `fredrin-agent restart` hand the machine
back to the per-job credential.

For the mechanism itself — the full credential ladder, and the four states
Fredrin's readiness probe reports — see
[GitHub CLI readiness](concepts/code/github-cli-readiness.md).

## 1. Install

The quickest way is to let **Claude Code** do it for you. On the machine you want
to run jobs (Claude Code is one of the supported coding CLIs, so it's often
already there — or SSH into the machine first), paste this prompt into Claude
Code:

```text
Set up the Fredrin remote agent on this machine so it can run my Fredrin tickets. Do it for me end to end:

1. FIRST, identify the Fredrin installation for this server. One installation runs ONE daemon serving EVERY workspace paired to that server; production and staging use separate installations. Pairing is additive. Check whether fredrin-agent is already installed or running, verify it is up to date, and update it when it is not. Keep an existing healthy installation running.
   - Find the launcher with `command -v fredrin-agent`; default state is under `~/.fredrin`. Named profiles have their own launcher under `agents/<profile>/bin` and their own launchd or systemd name.
   - Run the selected launcher's `status` and `service status`. Use that SAME launcher for every remaining command; generic `fredrin-agent --profile <name>` also selects its installed bundle. If the intended profile, owner, or server is ambiguous, ask me to identify it before changing anything.
   - Before introducing profiles under one uid, every running daemon and repair CLI must support `installation-protocol` version 1. Older doctor commands cannot distinguish installations. Use separate service users if that prerequisite is not met. Profiles do not isolate arbitrary Worker code or third-party credentials.
   - Check the build with `fredrin-agent update --check`; `fredrin-agent version` reports installed bytes. If it is ALREADY up to date: leave it running — do NOT uninstall, remove, or reinstall it. Skip the prerequisites and installer (steps 3–4), run the platform check (step 2), then verify (step 5).
   - If a newer build is available (the bundle is STALE): update it IN PLACE with the selected `fredrin-agent update`, then continue at step 2. Drain that installation's active work before `fredrin-agent restart`; other installations keep running. Windows stages the pair and applies it on the next start. Preserve agent.json, credentials and machine-id.
   - If update fails, inspect selected `status`, `logs`, and `doctor`. A mixed-origin migration-required result needs the operator runbook at /docs/remote-agent, not a reinstall. For a necessary reinstall, drain only this installation, run its `service uninstall` before `stop`, then verify its supervisor and owned PID are stopped. Unknown processes require operator attribution; never terminate processes by a machine-wide name match.
   - A new named installation requires an explicit FREDRIN_AGENT_PROFILE and FREDRIN_URL. FREDRIN_AGENT_WORKER_LIMIT is OPTIONAL: leave it unset for no limit, which is the default, and set it only if I give you a budget measured from host capacity. Its native installer preserves the global default launcher and installs a private bundle. If no installation exists, continue at step 2.
2. Check WHICH USER will run the agent, before installing anything — it decides whether this machine can run ANY Claude ticket. Run `id -u`:
   - It printed something other than 0 (a normal account): there is nothing to do here — go straight to step 3.
   - It printed 0 (you are root): every unattended Fredrin Worker launches its coding CLI with `--dangerously-skip-permissions`, and Claude Code REFUSES that flag under root unless a sandbox is declared. Left alone, this machine will install, pair and report healthy, and then fail every single Claude ticket before the CLI even starts. Fix it NOW, as part of this setup — do not ask me first, and do not wait to see the error:
     a. If this box has a normal login account, prefer it: `getent passwd | awk -F: '$3>=1000 && $3<65534 {print $1}'` lists the candidates. If there is one, run every remaining step as that user (`sudo -u <user> -i`) and skip b–c.
     b. Otherwise root is the only account — a microVM, a container, or a cloud image at first boot, where the VM/container boundary IS the isolation boundary. Declare that to Claude Code, for this shell and for future ones:
          export IS_SANDBOX=1
          grep -qs '^IS_SANDBOX=' /etc/environment || echo 'IS_SANDBOX=1' >> /etc/environment   # Linux; skip if there is no /etc/environment
     c. The supervisor gets it automatically: `fredrin-agent service install` (which `fredrin-agent login` runs for you) writes `Environment=IS_SANDBOX=1` into the unit whenever the daemon will run as root, and regenerates it on every install and self-update. So do NOT hand-write a systemd drop-in, and do NOT edit the unit file — an edit is exactly what a later self-update overwrites.
3. Check the prerequisites and install anything missing: Node.js 20+ (with npm) and the GitHub CLI `gh` INSTALLED. Do NOT run `gh auth login` on this machine, and do not tell me to: `gh` does not need to be signed in, because Fredrin supplies the GitHub credential with every job (the run executor's), so this machine can hold no GitHub secret at all. A login here would override that credential for every job — the rule and what it costs are at https://app.fredrin.com/docs/remote-agent#which-github-account-jobs-run-as. For the Claude runtime a machine with a signed-in Claude Code just uses that login; a machine without one cannot run Claude tickets, so sign Claude Code in here if this box will run any. Only if I plan to run Cursor or Codex tickets on this machine does that CLI need its own sign-in.
4. Install (or update) the agent by running the official installer — it downloads the CURRENT self-contained bundle and puts a `fredrin-agent` launcher on my PATH, replacing any older bundle:
     curl -fsSL https://app.fredrin.com/install.sh | sh
   (use `wget -qO- https://app.fredrin.com/install.sh | sh` if curl isn't available)
5. Verify it installed by running `fredrin-agent status` and show me the output.

Then tell me the next step: pairing this machine with my workspace by running `fredrin-agent login` (I'll grab the pair code and workspace id from Settings → Remote agents → Pair new agent). Pairing is additive — running `login` for another workspace ADDS it alongside the ones this machine already serves; it never removes them. If this machine was already paired and you only updated it in place, run `fredrin-agent restart` instead to bring the freshly-installed version online.
```

Claude Code first checks for an existing `fredrin-agent`: an up-to-date one is
left running (one installation serves its paired workspaces, so adding another is additive),
a stale one is updated in place, and only a missing one is installed fresh. It
then resolves the root case (step 2 — a no-op unless you are root; see
[Running as root](#running-as-root)), checks the prerequisites, runs the
installer if needed, and confirms the agent is on your PATH. Then you pair it in step 2 below. In the **Pair new agent**
dialog, Fredrin hands you the same prompt with your pair code and workspace id
already filled in, so Claude Code installs *and* pairs in one paste.

**Prefer to run it yourself?** The installer is a plain one-liner. Pick the one
for the machine that will *run jobs*, which is not always the one you are
reading this on:

```bash
# macOS / Linux
curl -fsSL https://app.fredrin.com/install.sh | sh
```

```powershell
# Windows, in PowerShell
irm https://app.fredrin.com/install.ps1 | iex
```

Either way, this downloads the bundled agent to `~/.fredrin/agent/`, installs the
prebuilt terminal engine beside it, and puts a `fredrin-agent` launcher on your
PATH. On macOS and Linux that is a `/usr/local/bin` symlink where the directory
is writable and `~/.fredrin/bin` otherwise — the installer prints which. It also
warns if `claude` or `gh` are missing.

The **Pair new agent** dialog shows whichever of the two matches your platform,
with a switch for the case where the machine you are pairing is not the machine
you are looking at.

### Windows

`install.ps1` is the PowerShell twin of `install.sh` and lands in the same
places, but a stock Windows box starts further back than a Linux one, so it does
more:

- **It installs Node.js 20+ and `gh` itself**, via `winget` and falling back to
  `choco`. Do not install them first; do not run `gh auth login` afterwards.
- **The launcher is `fredrin-agent.cmd`**, in `%USERPROFILE%\.fredrin\bin`.
  Windows has no shebang handling and no PATHEXT entry for an extensionless
  file, so a bare `fredrin-agent` there would be a *document* — double-clicking
  it raises the "Select an app to open" picker rather than running anything. A
  POSIX twin is written beside it so the bare name also works in Git-Bash.
- **That directory goes on your user PATH** (appended, never reordered) *and*
  on the current session's PATH, so `fredrin-agent status` runs in the same
  PowerShell window, with no restart and no sign-out.
- **Git for Windows is a hard requirement for running jobs.** The agent runs
  every job's coding CLI inside a `bash -l` login shell — the launch command is
  POSIX-quoted and the per-ticket wrapper and lifecycle hooks are `sh` scripts —
  so without Git-Bash every ticket dies at spawn with exit code 127 on a machine
  that looks perfectly healthy on the board. Install it from
  <https://git-scm.com/download/win>. Do not substitute WSL:
  `C:\Windows\System32\bash.exe` is a different machine, and Fredrin will not
  use it.
- **`login` installs a supervisor here too**, as it does everywhere else — a
  **Task Scheduler** task named `FredrinAgent` that starts the agent at boot and
  at logon and restarts it if it crashes. There is no `fredrin-agent start` to
  run after pairing, and none after a reboot. See
  [Supervision](#supervision-installed-by-login).

Re-running the installer upgrades the bundle in place. It never touches
`~/.fredrin/agent.json`, so every workspace the machine is paired to survives.

## 2. Pair with Fredrin

1. In Fredrin, open **Settings → Remote agents** and click **Pair new agent**.
   The dialog hands you one copyable prompt, and both credentials ride inside
   it: its `fredrin-agent login` step already carries them, and its last two
   lines spell out the 8-character **pair code** (like `ABCD-EFGH`) and your
   **workspace id** for reading off by hand. The **macOS / Linux · Windows**
   switch above the prompt picks which machine it is written for — it starts on
   your own platform, so change it if you are pairing a different one.
2. On the machine, run:

   ```bash
   fredrin-agent login
   ```

   You'll be prompted for:
   - **Pair code** — the prompt's second-to-last line. It does not expire, so
     take as long as the machine needs; it is single-use, so mint a fresh one
     for the next machine
   - **Workspace id** — its last line
   - **Repo root** — optional; press Enter to accept the default. Pairing is
     machine-wide: the agent serves every project in the workspace and clones
     each project's repo on demand (a local checkout is only reused when its
     `origin` matches the ticket's project), so there is no project to pick here

   **Scripting it?** On a machine nobody is typing at — a provisioning script, an
   SSH one-liner, a Claude Code session — pass the answers as flags instead, and
   the prompts are skipped entirely:

   ```bash
   fredrin-agent login --code ABCD-EFGH --workspace your-workspace-id
   ```

   (`FREDRIN_PAIR_CODE` / `FREDRIN_WORKSPACE_ID` in the environment do the same.)
   Do **not** try to pipe answers into the interactive prompts: a pipe hits EOF,
   readline drops everything after the first line, and `login` exits 0 having
   paired nothing.

3. On success, `login` installs the agent as a **service** (a launchd
   LaunchAgent on macOS, a systemd user unit on Linux) so it starts at
   boot/login and restarts within seconds of a crash — and starts it. On an
   interactive terminal it asks first (default **Y**); non-interactive logins
   install without asking. Pass `--no-service` to skip and manage the process
   yourself.

4. The dialog confirms the moment pairing succeeds, and the machine appears in
   the Remote agents list.

**Pairing is additive.** One machine runs one `fredrin-agent` daemon that serves
every workspace it is paired to. Running `fredrin-agent login` for a *new*
workspace ADDS a pairing — the workspaces you paired earlier keep claiming jobs
uninterrupted; running it again for a workspace you already paired just rotates
that one credential in place. `fredrin-agent status` lists every workspace this
machine serves and whether each is online. (Older agents held a single pairing,
so pairing to a second workspace used to overwrite the first — that is fixed.)

Credentials for every pairing are stored at `~/.fredrin/agent.json` (mode `0600`)
as a `pairings` array; a legacy single-pairing file is migrated automatically on
first read. The server URL is always `app.fredrin.com`; `FREDRIN_URL` exists only
as a dev/preview override.

## 3. Manage the running agent

After login the agent is already running under supervision — there is no
separate start step. Manage it with:

```bash
fredrin-agent status    # running? paired as what? where are the logs?
fredrin-agent logs      # last 200 log lines (-f to follow)
fredrin-agent restart   # supervised: supervisor respawns it; bare: stop + start
fredrin-agent stop      # graceful shutdown (releases in-flight jobs)
```

If you opted out with `--no-service`, `fredrin-agent start` from an interactive
terminal detaches into the background and returns immediately — closing the
SSH session doesn't stop it. Nothing restarts a bare daemon after a crash,
and `start` says so on stderr.

Use `fredrin-agent start --foreground` to keep it attached (for debugging).
Under systemd or pm2 it detects the supervisor and stays in the foreground
automatically, so service units keep working unchanged.

The agent connects, probes which coding CLIs are installed, reports them to
Fredrin, and waits for jobs. Pick the default CLI and model per workspace in
**Settings → Runtimes**. When a ticket is dispatched to this machine, the agent
creates a worktree and branch for the ticket, runs the configured CLI in a real
interactive terminal, and streams output (and your typed input) through the
ticket UI.

Restart the agent (`fredrin-agent restart`) after installing a new CLI, or after
changing this machine's `gh` login, so it re-reports its capabilities.

### Supervision (installed by login)

`fredrin-agent login` installs the service by default (see step 2 above). The
`service` verbs manage it directly — including installing later if you opted
out at login:

```bash
fredrin-agent service install     # start at boot + restart on crash
fredrin-agent service status      # supervisor state (plus plain daemon state)
fredrin-agent service uninstall   # stop supervising; removes the unit
```

Logs still land in `~/.fredrin/agent.log` (`fredrin-agent logs`). On Linux the
unit runs in your user session; `service install` prints the
`loginctl enable-linger $USER` command that makes it start at boot *before*
you log in. Re-running `service install` is safe — it regenerates the unit
with the current node/agent paths.

On a headless Linux box with no user session (a VM you only reach over `sudo`,
or one nobody logs into), `service install` repairs the missing
`XDG_RUNTIME_DIR` when a user manager is running, and otherwise installs a
**system-wide** unit at `/etc/systemd/system/fredrin-agent.service` — still
running as your account, and starting at boot without lingering. That needs root
once, non-interactively: run `sudo -v && fredrin-agent service install`. See
[Agent service supervision](concepts/code/agent-service-supervision.md).

Running under your own systemd system unit or pm2 still works unchanged — the
agent detects those supervisors and stays in the foreground.
(`pm2 start fredrin-agent -- start` also works.)

### Windows: Task Scheduler

`service install` registers a **Task Scheduler** task called `FredrinAgent`,
plus the `.cmd` it runs — `%USERPROFILE%\.fredrin\fredrin-agent-service.cmd`,
which is what `service status` reports as the unit path. Task Scheduler is
preferred over NSSM for the same reason launchd is on macOS: it is on every
Windows install and needs nothing downloaded.

The task runs as **you**, unelevated — never as `SYSTEM`, which would have
neither your `~/.fredrin` pairing nor your CLI logins. The `.cmd` is the restart
loop: Task Scheduler only restarts a task that *fails*, and a supervised
self-update exits 0 on purpose, so a restart-on-failure policy alone would leave
the machine dark after every update.

Two things are worth knowing:

- **Where it starts from.** The task is registered "run whether I am logged on
  or not" (`S4U`), which is what gives it a **boot** trigger — the Windows
  equivalent of `loginctl enable-linger`. That needs the *Log on as a batch job*
  right, which a standard account may not have; when the registration is
  refused, install falls back to a **logon-only** task and says so. Check which
  one you got with `fredrin-agent service status` — it prints either
  `Starts at: boot and login` or `Starts at: login only`.
- **`service uninstall` removes the task first, then stops the daemon.** In the
  other order the restart loop would simply bring it back.

### Running as root

Claude Code refuses `--dangerously-skip-permissions` under uid 0 unless a sandbox
is declared, and that flag is what every unattended Worker launches with. So a
daemon running as root with nothing declared **pairs, reports healthy, and then
fails every Claude ticket before the CLI starts** — `fredrin-agent status` is
green the whole time, because the refusal only bites once a job is dispatched.

Where the machine has a normal login account, run the agent as that account and
there is nothing to do. Where root is the only account — a Firecracker microVM, a
container, a cloud image at first boot — the VM boundary *is* the isolation
boundary, and `service install` declares it for you: the unit it writes carries

```ini
Environment=IS_SANDBOX=1
```

whenever the daemon will run as root. It is regenerated on every install and on
every self-update, so unlike a hand-written drop-in it cannot be silently dropped
— **do not** edit the unit or add a `fredrin-agent.service.d/` override yourself.
`sudo fredrin-agent service install` is unaffected: the unit still runs as the
invoking account, which is not root, and gets no declaration.

To check what the running daemon actually has (Linux):

```bash
tr '\0' '\n' < /proc/$(cat ~/.fredrin/agent.pid)/environ | grep -c '^IS_SANDBOX=1$'
```

`1` is correct. `0` means the daemon predates the declaration — run
`fredrin-agent service install` and `fredrin-agent restart`.

### Self-update

Once installed, the agent keeps itself current: while idle it checks Fredrin
for new builds, swaps in the new bundle atomically, and relaunches. You install
once; there is no manual upgrade step.

On **Windows** the swap is *deferred* rather than skipped: the validated bundle
is staged next to the running one, and the next `fredrin-agent start` applies it
before the daemon opens anything, then re-launches onto it. So the box still
updates unattended — it just does the file replacement in the one window Windows
allows it. `fredrin-agent update` there prints "Staged … — restart to load it"
instead of "Updated". The thing that provides that next start is the Task
Scheduler task above: an unsupervised Windows box stages an update it never
applies. See [Agent self-update](concepts/code/agent-self-update.md).

## Troubleshooting

- **Disk fills with old Worktrees** — the Agent cleans completed/archived tickets
  at startup, hourly, and after runs. It removes proven clean checkouts and evicts
  known ignored build caches when source, missing upstreams, or unknown ignored
  files require keeping the checkout. `fredrin-agent logs` shows `[worktree-gc]`
  outcomes and the path/reason for failures; an unavailable server retains work
  until a later pass. Active and Review Worktrees remain intact. Unknown ownership
  and orphan folders require explicit attribution; see
  [Worktree retention](concepts/code/worktree-retention.md). The existing
  `fredrin-agent worktrees gc --dry-run` command reports server-eligible candidates,
  before Git safety checks decide removal or cache eviction.
- **"Not paired. Run: fredrin-agent login"** — `~/.fredrin/agent.json` is
  missing or unreadable; re-run `fredrin-agent login`.
- **Pair code rejected** — a code is single-use, so one that already paired a
  machine is spent, and a mistyped one never existed. Neither is fixed by
  waiting or by retrying the same code: read it off the prompt again, or mint a
  fresh one from **Settings → Remote agents → Pair new agent**. A code does not
  go stale on its own, so if the machine is simply not there yet, keep the code.
- **`service install` fails with "Failed to connect to bus: No medium found"** —
  `systemctl --user` needs a user D-Bus session, and your shell has none:
  `sudo`/`su` create no login session (so `XDG_RUNTIME_DIR` is never set), and a
  box nobody logs into never starts a user systemd at all. Pick one:
  `sudo loginctl enable-linger $USER` and re-run; `sudo -v && fredrin-agent
  service install` to install a system-wide unit instead; SSH in as a real
  session rather than `sudo su`; or `sudo apt-get install -y dbus-user-session`.
  The command prints these itself. If `fredrin-agent status` says *"unit file
  written but NOT registered with systemd"*, this is why.
- **Terminal engine install fails** — your platform may lack a prebuilt
  `node-pty` binary; install build tools and re-run the installer
  (Debian/Ubuntu: `sudo apt-get install -y build-essential python3`).
- **Jobs dispatch but the CLI hangs or PRs never open** — check that the coding
  CLI is signed in (`claude /login`, `agent login`, …) **as the user the agent
  runs as**; logins are per-user. `gh` needs no login of its own — Fredrin sends
  the credential with the job.
- **Every job on this machine dies at clone with "Could not resolve to a
  Repository"** — the repo exists and you can see it, so this machine is acting
  as a different GitHub account: it has its own `gh` login, and that account
  cannot see the repo. `gh auth logout` and `fredrin-agent restart` hand it back
  to the per-job credential; see **Which GitHub account jobs run as** under
  Prerequisites.
- **A newly installed CLI isn't offered** — restart the agent
  (`fredrin-agent restart`) so capabilities are re-probed.
- **`--dangerously-skip-permissions cannot be used with root/sudo privileges for
  security reasons`** — every Claude job fails instantly, either with that from
  the CLI or with Fredrin's own "Claude Code refuses
  `--dangerously-skip-permissions` under root". The daemon is running as uid 0
  with no sandbox declared. Re-run `fredrin-agent service install`, which writes
  the declaration into the unit, then `fredrin-agent restart`, and re-check with
  the command under "Running as root" above.
