Skip to content

Secret stores

The runner reads your service credentials from your secret manager, at boot, and keeps them in memory only. Nothing is written to disk, and nothing is custodied outside your infrastructure.

Terminal window
RUNNER_SECRET_STORE_MODE=env # default, development
RUNNER_SECRET_STORE_MODE=aws # AWS Secrets Manager
RUNNER_SECRET_STORE_MODE=vault # HashiCorp Vault KV v2

All three backends implement the same minimal interface, get(name), so switching between them changes nothing else in the runner.

Reads from the process environment. It’s the default, and it’s what Compose feeds through pass-through or runner.secrets.env.

Fine for local development. In production, prefer one of the two below.

Terminal window
RUNNER_SECRET_STORE_MODE=aws
# RUNNER_SECRETS_MANAGER_REGION=us-east-1 # absent ⇒ SDK default resolution
# RUNNER_SECRETS_MANAGER_PREFIX=rootpilot/ # prefixes the SecretId

Resolves through the SDK’s default credential chain (IAM role, workload identity, IRSA) with no static keys. It’s the natural path for a runner on EKS or ECS.

PREFIX prefixes the SecretId, so a secret named DD_API_KEY with prefix rootpilot/ is fetched as rootpilot/DD_API_KEY.

Terminal window
RUNNER_SECRET_STORE_MODE=vault
RUNNER_VAULT_ADDR=https://vault.example.com
# token auth
RUNNER_VAULT_AUTH=token
RUNNER_VAULT_TOKEN=
# or AppRole auth
RUNNER_VAULT_AUTH=approle
RUNNER_VAULT_ROLE_ID=
RUNNER_VAULT_SECRET_ID=

HTTP access to the KV v2 API. Additional options:

Variable Default What for
RUNNER_VAULT_NAMESPACE none Vault Enterprise.
RUNNER_VAULT_KV_MOUNT secret KV v2 mount.
RUNNER_VAULT_KV_FIELD value Which field of the secret to return.
RUNNER_VAULT_PREFIX '' Prefixes the logical path.

The schema refuses incomplete configuration at boot:

RUNNER_SECRET_STORE_MODE=vault requires RUNNER_VAULT_ADDR
vault auth requires RUNNER_VAULT_TOKEN (token) or RUNNER_VAULT_ROLE_ID+RUNNER_VAULT_SECRET_ID (approle)

In both remote backends, “not found” (ResourceNotFoundException on AWS, 404 on Vault) resolves to undefined, not an exception.

That’s what makes the lazy model work: you configure only the connectors you use, and the rest simply drop out of the served set. A missing secret does not bring down the boot.

The signal is in the boot log:

connector not served {connectorId: "azion", missing: ["AZION_TOKEN"]}

The runtime scopes the secret store per connector: each one sees only the secrets it declared in its own manifest. A connector cannot read another’s credential, even running in the same process. It’s the first of the runtime’s four guarantees.