Repository navigation
Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
✅ (prod) | — | PostgreSQL connection string for the controller's metadata DB. |
BETTER_AUTH_SECRET |
✅ | dev-insecure… |
Long random string for session signing. Also the fallback for MASTER_KEY. AUTH_SECRET is accepted as an alias. |
BETTER_AUTH_URL |
✅ | http://localhost:3000 |
The public URL the app is served at (used by auth + links in alerts). APP_URL is accepted as an alias. In production it is the only origin trusted for sign-in requests. |
MASTER_KEY |
recommended | falls back to BETTER_AUTH_SECRET
|
Base64, 32 bytes. Encrypts all secrets at rest (and restic repo passwords). Back this up. |
AGENT_IMAGE |
— | ghcr.io/holo795/cbm-agent |
Agent image the install command / /install.sh tells hosts to run. |
AGENT_IMAGE_TAG |
— | latest |
Tag for the agent image. |
COOLIFY_API_RATE_LIMIT |
— | 120 |
Calls per minute CBM allows itself towards each Coolify instance. Coolify limits its API per user (its own API_RATE_LIMIT, 200 a minute by default); a call that still gets "429 Too Many Attempts" waits what Coolify asks and is retried. Lower it if you use the same Coolify token elsewhere. |
AGENT_CONTROLLER_URL |
— | falls back to BETTER_AUTH_URL
|
URL agents dial to reach the controller, if it differs from the browser URL (e.g. http://host.docker.internal:3000 in local dev). |
PASSWORD_BREACH_CHECK |
— | true |
Refuse new passwords (sign-up, change, reset) that appear in a known breach, via HaveIBeenPwned's k-anonymity API (only the first 5 characters of the password's SHA-1 leave the server). If the API can't be reached within 3 s the password is accepted. false turns it off. |
Generate secrets:
openssl rand -hex 32 # BETTER_AUTH_SECRET
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" # MASTER_KEYSince 2.1, a production controller (NODE_ENV=production, as in the image) refuses to start
rather than run insecurely:
-
BETTER_AUTH_SECRETmissing or left at the public default — anyone could forge a session cookie. -
MASTER_KEYset but not a base64-encoded 32-byte key — it used to be ignored silently in favour of the key derived fromBETTER_AUTH_SECRET; fixing it later would then have made every stored secret undecryptable. LeavingMASTER_KEYempty is still allowed (the derived key is used).
Upgrading an install that ran with the default secret and no MASTER_KEY. Its stored secrets
(Coolify tokens, destination credentials, keys) were encrypted with the key derived from the
default secret. Set MASTER_KEY to that key first, so they keep decrypting, then set a real
BETTER_AUTH_SECRET (everyone signs in again):
MASTER_KEY=Sim+zqQAguUSPC+1o13hyzhjra/Ul+BPul7ZsVI6ghs= # derived from the public defaultThat key is derived from a public string, so it protects nothing. Once the controller is up, plan a move to a fresh key by re-entering your secrets.
A failed database migration also stops the container now, instead of starting the app on an out-of-date schema.
Single sign-on is configured in Settings → Single sign-on (see
Accounts & roles). These variables configure a provider as code
instead: a provider set here wins over its Settings form, which then shows it locked.
GITLAB_ISSUER is only needed for a self-managed GitLab (default https://gitlab.com). The
callback URL to register with the provider is <BETTER_AUTH_URL>/api/auth/callback/<provider>
(github, google, gitlab or oidc).
| Provider | Variables |
|---|---|
| GitHub |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
|
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
|
|
| GitLab |
GITLAB_ISSUER, GITLAB_CLIENT_ID, GITLAB_CLIENT_SECRET
|
| OpenID Connect (Authentik, Keycloak…) |
OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_LABEL (the button's label) |
Optional — enables password reset, account verification, and emailed invitations. You can also set these from Settings → Email (SMTP) in the UI; any value set here overrides the UI and locks that field (config-as-code wins). Env-provided SMTP is trusted (no "send test" needed). Full guide: Email (SMTP).
| Variable | Default | Description |
|---|---|---|
SMTP_HOST |
— | SMTP server hostname, e.g. smtp.mailgun.org. |
SMTP_PORT |
— |
587 (STARTTLS) or 465 (implicit TLS). |
SMTP_SECURE |
— |
true for implicit TLS (port 465). Unset: the UI setting, else true only on port 465. |
SMTP_USER / SMTP_PASSWORD
|
— | SMTP credentials. |
SMTP_FROM |
— | From address, e.g. backups@yourdomain.com. |
SMTP_FROM_NAME |
— | Optional display name, e.g. CBM Backups. |
Most are set by the install command; you rarely set them by hand. The ones marked (CBM) can also be changed from CBM (Agents → Default settings, or the gear on an agent's row): CBM's values apply on the next heartbeat, without a restart. A variable set on the host wins over CBM — the field is then locked in the dialog and shows the host's value.
| Variable | Default | Description |
|---|---|---|
CONTROLLER_URL |
http://localhost:3000 |
Where the agent reaches the controller. |
ENROLLMENT_TOKEN |
— | The instance's enrollment token (instance card → … → Install command); exchanged for a bearer token on first start. |
AGENT_TOKEN |
— | Bearer token (set automatically after enrollment). |
AGENT_HOSTNAME |
OS hostname | Identifies this agent (one agent per instance + hostname). |
AGENT_SERVER_UUID |
— | Pin this agent to a Coolify server (disables auto-detection). Usually left unset. |
AGENT_CONCURRENCY (CBM)
|
2 |
How many jobs the agent runs at once (1–16). |
AGENT_WORK_DIR |
/var/lib/cbm-agent (image) |
Local staging directory for artifacts before upload (/tmp/cbm-agent when run outside the image). |
AGENT_MIN_FREE_MB (CBM)
|
1024 |
Free space kept on the work dir's disk: a backup checks it before freezing anything, a local copy never eats into it, and a restore/mirror/drill needs twice the snapshot size on top. |
AGENT_STAGING_MODE (CBM)
|
auto |
How volumes are copied. tar: auto (through the work dir when it has room, otherwise straight to the destination), local (always through the work dir; refuse when it doesn't fit) or direct (always straight to the destination). restic: auto and direct read each volume in place (no copy, only changed files), local copies it to the work dir first. See Backups → Disk space on the agent host. |
DOCKER_BIN |
docker |
Path to the Docker CLI. |
POLL_INTERVAL_MS |
5000 |
Job poll interval. |
HEARTBEAT_INTERVAL_MS |
30000 |
Heartbeat interval. |
LOG_LEVEL (CBM)
|
info |
debug, info, warn or error. |
AGENT_FREEZE_METHOD (CBM)
|
pause |
How containers are frozen during a copy: pause (docker pause) or cgroup (the same freeze, invisible to Docker, so a health-checked container stays healthy behind Coolify's proxy; needs privileged containers). See Backups → Freezing and health checks. |
RESTIC_READ_CONCURRENCY (CBM)
|
2 |
Files restic reads at once (1–32). Raise it on fast disks (NVMe), e.g. 4 to 8. |
RESTIC_PACK_SIZE (CBM)
|
16 |
Size of the packs restic writes, in MiB (4–128). Bigger packs mean fewer files on a remote (SFTP, S3), e.g. 64, at the cost of memory. |
RESTIC_CACHE_DIR |
<work dir>/restic-cache |
restic's cache of the repositories' index. In the work dir (a persistent volume), so it survives agent updates and restic doesn't download the index again on every run. |
The agent container must mount the Docker socket and (for "local" destinations) a persistent
/backups volume — the install command does both.
- Settings → Timezone — IANA timezone used to evaluate schedules (cron) and display every timestamp. Stored server-side, the same for everyone.
- Settings → Failure alerts — a webhook URL (Discord / Slack / custom) notified on backup failures, missing backups, and overdue backups. See Alerts.
- Settings → Email (SMTP) — SMTP details (unless set by env), a test send, and the "Require email verification" switch. See Email.
- Settings → Disaster recovery — the metadata self-backup and the recovery file. See Disaster recovery.
- Settings → Restore drills — "Run a drill every week". See Restore.
- Settings → API tokens (MCP) — New token creates a machine token with its own role. See MCP server.
Settings is admin-only; on wide screens a sticky nav on the left jumps between these sections.
These pages are generated from docs/ on every change to main: edit the files there (a change made here is overwritten).
Getting started
Backups
Running CBM