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.
What it never does
Section titled “What it never does”- 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
roottoken. - 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.
What you need
Section titled “What you need”- 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,writeandread:sensitivetokens must be created by a team admin or owner.
The tokens
Section titled “The tokens”| 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.
How Coolify becomes a town
Section titled “How Coolify becomes a town”| 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.
The webhook
Section titled “The webhook”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 |
Polling
Section titled “Polling”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.
Known limits
Section titled “Known limits”- 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.