Production deployment
npm run fleet:up # anonymous fleet, synthetic connectorsRUNNER_TENANT=<slug> npm run fleet:up # a tenant's fleet, real connectorsfleet:up applies docker-compose.fleet.yml as an overlay on the development Compose file. Every
difference between the two exists for a specific reason.
What changes, and why
Section titled “What changes, and why”The tunnel
Section titled “The tunnel”wss://tunnel.rootpilot.sh:8443 is the runners’ tunnel port. Other control-plane ports and hostnames
will not accept a runner.
No dev-certs
Section titled “No dev-certs”The overlay redefines volumes with !override, and not for style: Compose merges volume
lists, so redefining without !override would leave dev-certs/ mounted. The entrypoint would then
set RUNNER_CERT_PEM from the development CA, the runner would take the provisioned-certificate
shortcut instead of enrolling, and mTLS would fail against the production CA with an error that says
none of this.
~/.aws stays mounted: it’s what backs RUNNER_AWS_CRED_MODE=chain.
restart: "no"
Section titled “restart: "no"”Connecting to production is a deliberate act. A runner that resurrects itself after a mistaken
docker compose down becomes a ghost presence in a real tenant’s Fleet view.
What RUNNER_TENANT does: and doesn’t
Section titled “What RUNNER_TENANT does: and doesn’t”It does not pick the tenant. The bootstrap token does that: it’s minted per tenant, and enrollment returns a certificate with that tenant’s SPIFFE id inside. Getting this slug wrong will not connect you to the wrong organization.
It does two things: it picks the credentials folder in the vault (/rootpilot/edge/<slug>) and it
turns on RUNNER_CONNECTORS=real. The second is deliberate: bringing up “organization X’s fleet” with
synthetic data is a no-op that looks like it worked, and that is the most expensive class of failure
this product has.
A note on posture
Section titled “A note on posture”For the actual BYOC path, what changes is where the container runs and how it proves identity:
- Identity without a human: swap
bootstrap-tokenfor a cloud identity:RUNNER_ATTESTATION_MODE=aws-stson EC2/EKS,gke-oidcon GKE. See Attestation. - Secrets from your vault:
RUNNER_SECRET_STORE_MODE=awsorvault, rather thanenv. See Secret stores. - Cloud credentials from the default chain:
RUNNER_AWS_CRED_MODE=chain(IRSA, instance profile) rather than static keys. See Cloud credentials.
Certificate renewal
Section titled “Certificate renewal”The enrollment certificate is valid for 1 hour. RUNNER_CERT_RENEWAL=on is the default, and it’s
what keeps a cattle fleet alive without human intervention. Without it, every runner has a one-hour
shelf life and only a human minting a bootstrap token brings it back.
Renewal requires a control-plane with the verifier. Against an older control-plane, renewal fails,
the runner degrades to the old behavior (expires in 1h), and logs cert renewal failed rather than
dying quietly.
When identity is lost for good
Section titled “When identity is lost for good”If the certificate goes past the point of recovery, the runner does not come back on its own, because the renewal attestation proves possession with the certificate that expired. The process then exits, so that the supervisor brings up a fresh runner that enrolls from scratch.
This is deliberate: without that exit, the process would become a zombie: up, serving nothing, and invisible to any probe, since the runner is an outbound client with no health endpoint.
Make sure your supervisor (Compose restart, a ReplicaSet, an ECS service) is configured to bring up
the replacement.