Notifications
Doco-CD can be configured to send notifications with Apprise to various services when a deployment is started, finished, or failed and on reconciliation events. You can find a list of all supported services and platforms in the Apprise documentation.
For that, specify the required settings in the app environment section and add an Apprise container to your docker-compose.yml file.
Settings¶
| Key | Type | Description | Default value |
|---|---|---|---|
APPRISE_API_URL |
string | The URL of the Apprise API to send notifications to (e.g. http://apprise:8000/notify) |
|
APPRISE_NOTIFY_URLS |
string | A comma-separated list of Apprise-URLs to send notifications to the supported services/platforms (e.g. pover://{user_key}@{token},mailto://{user}:{password}@{domain}) |
|
APPRISE_NOTIFY_URLS_FILE |
string | Path to the file inside the container containing the Apprise-URLs (see APPRISE_NOTIFY_URLS). Mutually exclusive with APPRISE_NOTIFY_URLS. |
|
APPRISE_NOTIFY_LEVEL |
string | The minimum level of notifications to send. Possible values: info, success, warning, failure |
success |
APPRISE_NOTIFY_REPEAT_INTERVAL |
duration | How long an unchanged failure notification is suppressed before it is sent again as a reminder (see Repeated failures). 0 sends every failure. |
1h |
APPRISE_NOTIFY_BODY_TEMPLATE |
string | Optional Go text/template rendering the notification body (see Custom notification body). Empty uses the built-in format. |
|
APPRISE_NOTIFY_BODY_TEMPLATE_FILE |
string | Path to a file inside the container containing the template (see APPRISE_NOTIFY_BODY_TEMPLATE). Mutually exclusive with APPRISE_NOTIFY_BODY_TEMPLATE. |
Example docker-compose.yml¶
Adjust your docker-compose.yml file to include the Apprise service and the necessary environment variables for the app:
services:
app:
container_name: doco-cd
# add the code below to your existing docker-compose.yml file
depends_on:
apprise:
condition: service_healthy
environment:
APPRISE_API_URL: http://apprise:8000/notify
APPRISE_NOTIFY_LEVEL: success
APPRISE_NOTIFY_URLS: "pover://{user_key}@{token},mailto://{user}:{password}@{domain}"
apprise:
image: caronc/apprise:latest
restart: unless-stopped
ports:
- "8000:8000"
environment:
TZ: Europe/Berlin
APPRISE_WORKER_COUNT: 1
healthcheck:
test: [ "CMD-SHELL", "curl -fsS http://localhost:8000/status >/dev/null || exit 1" ]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
Repeated failures¶
A failure repeats as often as its trigger. A poll job runs on its interval and a broken deployment fails the same way every run, so one fault produces one message per poll until somebody fixes it - an expired registry token can turn into hundreds of identical notifications overnight.
Failure notifications are therefore de-duplicated per stack:
- The first failure of a stack is sent immediately.
- While the failure stays the same, further notifications are skipped and repeated
only once per
APPRISE_NOTIFY_REPEAT_INTERVAL, so a long outage stays visible without flooding. - A different error on the same stack is sent right away - it is new information.
- A successful notification for that stack (e.g.
Deployment completed) clears the suppression, so it also acts as the recovery signal and the next failure is sent immediately. - Set
APPRISE_NOTIFY_REPEAT_INTERVALto0to send every failure, unchanged or not.
Stacks are tracked separately per repository, target and Docker context, and the state is in memory: a restarted daemon sends the next failure again.
For the same reason a failed deployment notifies once, not twice. The stack reports its own failure, and the poll job or webhook that received the same error no longer repeats it under its own title. Logs and metrics are unchanged.
Metadata fields¶
When a notification is sent, the following metadata fields are included in the notification body:
| Field name | Description | Example |
|---|---|---|
duration |
Time the deployment took (omitted when no deployment ran, e.g. for reconciliation notifications) | 12.483s |
job_id |
Unique ID of the deployment job that triggered the notification (not included for reconciliation notifications) | |
repository |
Repository name | github.com/my/repo |
revision |
Branch/tag and Commit SHA that was deployed | main (abc123), v1.0.0 (def456) |
stack |
Project/Stack name | my-stack |
Custom notification body¶
By default the notification body is the message followed by the metadata fields as key: value lines. Set APPRISE_NOTIFY_BODY_TEMPLATE (or APPRISE_NOTIFY_BODY_TEMPLATE_FILE) to a Go text/template to render the body yourself — useful to drop noisy fields, add a host label, or produce a one-liner when several stacks report into one channel.
The template is validated at startup: a syntax error or a reference to an unknown field stops doco-cd from starting. The title (emoji + optional [R] marker + title text) is not affected by the template.
Don't swallow failure reasons
The template replaces the default body for every notification level, and on failure the error text is only carried by {{ .Message }} — a template that never references it produces failure notifications with no failure reason at all (the title only says Deployment failed; the details then live only in the logs). On success, .Message is largely redundant with the title, so compact templates should include it guarded by level:
The following fields are available:
| Field | Description |
|---|---|
.Level |
info, success, warning or failure |
.Emoji |
Level emoji (ℹ️/✅/⚠️/❌) |
.Title |
Title text, e.g. Deployment completed |
.Message |
Core message |
.IsReconciliation |
true when triggered by a reconciliation event |
.Repository |
Repository name |
.Stack |
Project/Stack name |
.Target |
Custom webhook/poll target (empty for the default target) |
.Context |
Docker context the stack is deployed to (empty for the default context) |
.Revision |
Branch/tag and commit SHA |
.JobID |
Deployment job ID (empty for reconciliation events) |
.ReconciliationEvent |
Reconciliation event that triggered the action |
.TraceID |
Reconciliation trace ID |
.AffectedActorKind |
container or service |
.AffectedActorID |
Affected container/service ID |
.AffectedActorName |
Affected container/service name |
.Commits |
Commits deployed since the last deploy (see Commit changelog) |
.Duration |
Time the deployment (or destroy) took, from job start to the notification, e.g. 12.483s. Zero where no deployment ran (reconciliation restarts, scheduled jobs) — hide it with {{if .Duration}}...{{end}} |
.ChangedServices |
Sorted names of the services changed by this deploy: force-recreated ones (changed mounted/referenced files or a changed auto-discovery label) plus services whose image digest changed in the registry (detected with force_image_pull). Empty when the whole stack is (re)deployed for other reasons, e.g. a compose config change (including image tag changes in the compose file), state drift or force_recreate |
{{ .DefaultBody }} renders the built-in body (message + metadata), so you can extend the default format instead of replacing it, e.g. {{ .DefaultBody }}\nhost: my-vm.
One-line body
environment:
APPRISE_NOTIFY_BODY_TEMPLATE: "{{.Emoji}} {{if .Target}}{{.Target}}/{{end}}{{.Stack}} — {{.Message}} ({{.Revision}})"
renders e.g. ✅ prod-vm/app — Successfully deployed stack app (main (abc123)) for target prod-vm, or ✅ app — Successfully deployed stack app (main (abc123)) without a custom target.
Commit changelog¶
.Commits is the list of commits that got deployed since the previously deployed commit, newest first. It is only populated on successful deploy notifications of Git sources; on the first deploy, on failures and for OCI sources it stays empty (a range over it just renders nothing). Each entry has:
| Field | Description |
|---|---|
.Hash |
Full commit SHA |
.ShortHash |
Shortened commit SHA |
.Subject |
First line of the commit message |
.Author |
Commit author name |
Printing an entry directly ({{ . }}) gives shortHash subject.
Body with changelog
Up to 50 commits are listed. After a rebase or force-push the list starts from the point where the histories diverged.
The changelog is best-effort: with a small GIT_CLONE_DEPTH the previously deployed commit may sit beyond the shallow boundary, in which case the list is truncated or omitted. It never blocks the notification.
Reconciliation notifications¶
If a notification was triggered by reconciliation, the title gets a short [R] marker.
Example notification titles
- Regular deploy notification title:
✅ Deployment completed - Reconciliation notification title:
✅ [R] Deployment completed
Reconciliation notifications also include a reconciliation: block in the body metadata.
Metadata fields¶
| Field name | Description |
|---|---|
event |
reconciliation event that triggered the action |
container_id |
affected container name |
container_name |
affected container name |
trace_id |
reconciliation trace ID for log correlation |
| Field name | Description |
|---|---|
event |
reconciliation event that triggered the action |
service_id |
affected service name |
service_name |
affected service name |
trace_id |
reconciliation trace ID for log correlation |