Skip to content

Docker Compose

Compose is the most direct way to run the Agent on a single host — a dedicated VM, for example. For orchestrators, see Kubernetes or the production setup.

services:
rootpilot-agent:
image: <address>/rootpilot-agent@sha256:<digest>
restart: unless-stopped
stop_grace_period: 150s
volumes:
- rootpilot-identity:/identity
environment:
RUNNER_TUNNEL_URL: wss://tunnel.rootpilot.sh:8443
RUNNER_ENROLL_URL: https://app.rootpilot.sh
RUNNER_ATTESTATION_MODE: bootstrap-token
RUNNER_BOOTSTRAP_TOKEN: ${RUNNER_BOOTSTRAP_TOKEN}
RUNNER_IDENTITY_DIR: /identity
RUNNER_INSTANCE_ID: agent-prod-1
RUNNER_CONNECTORS: real
RUNNER_SECRET_STORE_MODE: aws
NODE_ENV: production
volumes:
rootpilot-identity:

stop_grace_period larger than RUNNER_DRAIN_TIMEOUT_MS (2 minutes by default), or SIGKILL lands mid-drain.

It is what makes docker compose pull && up -d not cost a new bootstrap token. Without it, the identity dies with the container and the previous token is already spent — it is single-use.

Prefer a named volume, as in the example above. The image already ships /identity owned by uid 1000 (it runs as non-root), and a named volume inherits that ownership when it is created — so it works without you doing anything.

A bind mount (./identity:/identity) inherits nothing: there, the ownership is the host directory’s, and it has to belong to uid 1000. Never mount it with :ro.

If the directory is not writable, boot does not fail — the Agent warns and carries on, and you only find out on the next deploy:

could not persist identity — next boot will re-enroll from scratch

The certificates, if you use a provisioned certificate

Section titled “The certificates, if you use a provisioned certificate”
volumes:
- ./certs:/certs:ro

The directory must hold runner.crt, runner.key, and ca.pem. The entrypoint reads them and exports them as RUNNER_CERT_PEM, RUNNER_KEY_PEM, and RUNNER_CA_PEM — the Agent reads PEM inline, not as a path, and mounting the directory avoids the nightmare of multi-line PEMs inside an env_file.

That path ignores enrollment and persisted identity: the two blocks above stop applying, and the certificate does not renew. See Identity and enrollment.

If the Agent runs on EC2 and uses the credential chain

Section titled “If the Agent runs on EC2 and uses the credential chain”

With RUNNER_AWS_CRED_MODE=chain, the SDK’s default chain resolves through the instance profile with no extra volume. If you instead use a local file-based profile, mount it:

volumes:
- ${HOME}/.aws:/home/node/.aws:ro

Without that volume the container cannot see the profile, and the chain fails without saying so — the classic “works in my terminal, not in the container”.