Skip to content

Coolify

The Coolify courier draws your Coolify as a town and runs the actions you allow. It reads the API of Coolify and receives its notifications: nothing is installed on the Coolify side, and nothing in your projects changes.

  • It never acts on its own, and a webhook never triggers an action. Actions come from a person in the town, in a district whose actions an admin turned on.
  • It never uses a more powerful token than the action needs, and it refuses a root token.
  • It never shows, logs or sends a token to the browser. Tokens are stored encrypted in the town hall, never in myrtille.yaml.
  • It never keeps what it does not need: every answer of Coolify is filtered by a list of allowed fields, so environment values, passwords, connection URLs, private keys, compose content, webhook secrets and tokens are dropped before anything else sees them.
  • Coolify 4.2.0 or later; 4.4.0 or later for previews.
  • The API enabled (Settings > Advanced) and reachable from the town hall.
  • One API token per permission, created in Keys & Tokens > API tokens. The deploy, write and read:sensitive tokens must be created by a team admin or owner.
Token Permission Used for
read read Required. The version, discovery, polling, the history of deployments, previews.
deploy deploy Optional. Deploy, redeploy, cancel, restart, stop, deploy a preview: the actions of operators.
write write Optional. Set environment variables: an action of admins. Values are write-only.
read:sensitive read:sensitive Optional. The latest lines of the logs of an application, a database or a preview.

Each action uses the token of its level, never a more powerful one. A citizen offers an action only when the token of its level is configured. The audit log records which token an action used by a digest, never its value.

When you save the tokens, the town hall checks each one: the read token must read the version of Coolify, and each token must hold the permission of its level and no more. A token holding both deploy and write, as a root token does, is refused as too powerful. These checks make Coolify log api.auth.ability_denied warnings: they are the expected refusals that prove a token holds only its own permission.

Without the read:sensitive token, the Logs tab of a citizen says which token is missing and links to the page of the resource in Coolify. With it, the town hall masks the secrets of the lines before it shows them, and logs are never stored.

Give the tokens in the setup of the town, or with myrtille init (they are typed without echo, or read from COOLIFY_READ_TOKEN, COOLIFY_DEPLOY_TOKEN, COOLIFY_WRITE_TOKEN and COOLIFY_READ_SENSITIVE_TOKEN). A stored token is never sent to another Coolify address: changing the address asks for every token again. myrtille doctor checks, in plain words, that the stored tokens still pass their checks, that the courier runs, and when its webhook last arrived.

Coolify Town
Project Building
Environment District
Application, service service citizen
Database database citizen
Server server citizen, in the first production district
Preview of a Git application preview citizen, in the previews district of the town plan

A container reads as running (running, starting), error (unhealthy, degraded, restarting), offline (exited, stopped, never started) or unknown (anything else). The town hall reconciles the whole instance every minute. A citizen Coolify stops reporting turns offline, then leaves the town 24 hours later.

Coolify sends its notifications to https://<town hall>/hooks/<courier id>/<token>, an address with a random token that the setup shows once. In Coolify, Notifications > Webhook: paste it, enable every event and save. Coolify refuses a private or loopback address unless it is allowed in its Settings > Advanced.

A webhook is only a hint: whoever holds its address can send one. So the outcome of a deployment, the reachability of a server, a backup and a scheduled task are confirmed through the API of Coolify before they reach the town, and dropped when the API does not confirm them. The text Coolify copies into some notifications (command output, error output) is never carried over: it may hold credentials. A notification Coolify sends again keeps the same idempotency key, so it counts once.

Coolify notification In the town
Deployment success or failure deploy.succeeded, deploy.failed or deploy.cancelled, read from the API
Status changed, restart limit reached container.stopped, container.restart_limit
Proxy restarted container.restarted on its server
Backup, volume backup backup.succeeded, backup.failed
Scheduled task task.succeeded, task.failed
Server reachable or unreachable server.reachable, server.unreachable
High disk usage server.disk_warning
Test nothing: it shows the webhook is configured

Coolify sends no webhook when a deployment starts, so the courier also polls, never more than 2 requests a second:

  • at rest, every 45 seconds: the running deployments, the statuses of applications, databases and services, and the servers;
  • while a deployment runs, and for a minute after an action launched from the town, every 3 seconds: the running deployments only.

At rest this costs about one request every 9 seconds. Change the cadence with poll.idleSeconds and poll.activeSeconds of the courier in the town plan.

  • A deployment shorter than 45 seconds whose webhook is lost gets no outcome event; the next reconciliation still shows its state.
  • The logs of a Coolify service (as opposed to an application) are not read yet: the town links to its page in Coolify.
  • Coolify notifies no restart after a crash. For a database, polling notices it; for an application, only the restart limit is notified, unless a Docker agent runs on its server and the Coolify courier names it in its agents: the agent then reports the crash within seconds.
  • A preview deploy from the town needs a Git application with a GitHub or GitLab App source.