Skip to content

Security

github-actions[bot] edited this page Oct 8, 2026 · 2 revisions

Security

Secrets at rest

All sensitive values — Coolify API tokens, SSH/S3 credentials, tar encryption keys, and restic repository passwords — are AES-256-GCM encrypted at rest with your MASTER_KEY (falling back to BETTER_AUTH_SECRET if unset).

Back up your MASTER_KEY. It is the root of all stored secrets and (with the restic engine) your backup encryption. If you lose it, encrypted data is unrecoverable. If it leaks, rotate credentials.

Backup encryption

  • tar engine — optional AES-256-GCM encryption of each artifact (a random key per destination, stored encrypted with MASTER_KEY).
  • restic engine — the repository is always encrypted with a random per-destination password (also stored encrypted with MASTER_KEY).

Protection against deletion

CBM holds every destination's credentials, so a compromised CBM could delete backups. Mark the destinations holding your second copy Protected (CBM never deletes there), and give S3 destinations a versioned bucket with a key that can't delete old versions — CBM checks it and alerts if it stops being true. See Retention → Protection against deletion.

Agents & enrollment

  • Agents authenticate to the controller with a bearer token, stored only as a sha256 hash in the database.
  • Enrollment tokens (instance card → … → Install command → "Reveal install command") are per-instance, shown once, and matched by hash. Revealing again rotates the token and invalidates the old one.
  • Agents make outbound connections only (they poll the controller) — nothing needs to be opened on your hosts.
  • Before a restore pins an image, the controller asks its registry (Docker Hub, ghcr.io…) whether the recorded digest still exists: an anonymous, read-only HEAD on the image manifest, nothing about your data. Without outbound access the digest is simply kept.

API tokens (MCP)

Machine tokens for the MCP server or any external agent are created in Settings → API tokens (MCP) → New token. Each carries its own role (viewer / operator / admin), is shown once at creation, and is stored only as a sha256 hash. Revoke a token from the same list; it stops working immediately. See MCP server.

Trust model

  • The agent mounts the Docker socket, which grants root-equivalent access to its host. Run agents only on hosts you trust, and treat the agent image like any privileged workload.
  • With the cgroup freeze method, the agent starts a short-lived privileged helper container (host PID namespace, the host's /sys/fs/cgroup, no network) for the time of a freeze; it only writes the freeze switch of the containers being copied. It adds no access the Docker socket doesn't already give.
  • An agent update from the controller only runs an image from the repository the agent already runs from (any other is refused by the agent itself), and only admins can start one. The updater container has no network.
  • The controller holds the metadata DB and the master key — protect it like any admin panel (TLS, restricted network, strong BETTER_AUTH_SECRET).
  • Backups can contain your application data and secrets; secure your destinations accordingly (encryption at rest, restricted access).

Accounts & roles

The first registered account is the administrator, after which public sign-up closes. Add OAuth providers (GitHub/Google/GitLab) if you prefer SSO — see Configuration.

Additional users join by invitation with a role — admin / operator / viewer. Role checks are enforced server-side on every mutating action (the UI only hides what a role can't use, as a convenience). Invitation links are single-use, expire after 48h, are bound to the invited email, and are stored only as a sha256 hash — the plaintext is shown once and never persisted. The last admin can't be demoted or removed. Full details: Accounts & roles.

Two-factor authentication (authenticator app + single-use backup codes) can be turned on per account and required by an admin for admins or for everyone. It is enforced after a password sign-in and after a GitHub / Google / GitLab sign-in (no session exists until the code is checked), wrong codes are rate-limited and lock the account for a while, and a code can't be replayed. TOTP secrets are encrypted with BETTER_AUTH_SECRET; backup codes are stored encrypted too. See Accounts & roles.

Email-bearing flows (password reset, verification, emailed invites) require SMTP; the SMTP password is encrypted at rest like every other secret. See Email.

Clone this wiki locally