Notifly

Dokploy alerts without the spam

Point Dokploy's Notifly channel at a digest workflow so a burst of build, backup and server alerts arrives as one digest email, grouped by `payload.projectName`. Setup, the 11 event types and their payload fields, and how to test with a burst.

Dokploy can send its deployment, backup and server alerts through a Notifly channel. Dokploy sends one trigger per event and does not deduplicate, digest or rate limit them, so a busy night of builds turns into one email per build. A digest step in the workflow Dokploy triggers collects those events and sends one summary instead.

This page gives you that workflow, the payload each Dokploy event carries, and a burst test.

Batching and the immediate restart were checked on 2026-10-30 on a Notifly dev stack. Real email delivery, a live Dokploy channel, and the "failures immediate" variant have not been checked yet. See the verification status at the end of the page.

Set up the Dokploy channel

In Dokploy, add a Notifly notification channel and fill in:

Dokploy fieldWhat to enter
API keyA Notifly secret key for the environment that holds the workflow.
Workflow keyThe workflowId of the digest workflow (dokploy-alerts-digest below).
Subscriber IDThe subscriber who should receive the digest. Leave it empty and Dokploy uses the subscriber ID dokploy.
Base URLLeave empty for https://api.notifly.io.

For every alert Dokploy sends POST /v1/events/trigger with the workflow key as name, the subscriber ID as to, and the event's fields as payload (see Workflows). Create the subscriber first, with an email address, or the email step has nowhere to send. See Subscribers.

All 11 event types go to the one workflow key, so the workflow tells them apart by the payload.event field.

The digest workflow

The workflow has three steps:

  1. Dokploy restart (immediate), an email step that runs only when payload.event equals dokploy.restart. A restart is the one alert you want right away.
  2. Digest, a regular digest with a 5 minute window, grouped by {{payload.projectName}}. It runs for every event except dokploy.restart.
  3. Dokploy alerts (digest), an email step after the digest that lists the batched events. It also runs for every event except dokploy.restart.

The first event of a group opens the digest and starts the 5 minute window. Events that arrive inside the window for the same subscriber and the same projectName join it instead of sending. When the window closes the steps after the digest run once, with every collected event available as steps.digest-step.events. Events without a projectName (docker.cleanup, dokploy.backup, server.threshold) have no group value to differ on, so they share one group. Read more in the digest notifications guide.

The conditions use step conditions: a skip rule that evaluates to true lets the step run. A skipped digest runs the next step straight away, which is why the digest email carries the same condition.

Create it with one request. POST /v2/workflows takes the same shape the dashboard editor saves:

curl -X POST https://api.notifly.io/v2/workflows \
  -H "Authorization: ApiKey $NOTIFLY_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Dokploy alerts (digest)",
    "workflowId": "dokploy-alerts-digest",
    "__source": "dashboard",
    "steps": [
      {
        "name": "Dokploy restart (immediate)",
        "type": "email",
        "controlValues": {
          "skip": { "==": [{ "var": "payload.event" }, "dokploy.restart"] },
          "subject": "Dokploy restarted",
          "editorType": "html",
          "body": "<p>{{payload.message | escape}}</p><p>{{payload.timestamp}}</p>"
        }
      },
      {
        "name": "Digest step",
        "type": "digest",
        "controlValues": {
          "skip": { "!=": [{ "var": "payload.event" }, "dokploy.restart"] },
          "type": "regular",
          "amount": 5,
          "unit": "minutes",
          "digestKey": "{{payload.projectName}}"
        }
      },
      {
        "name": "Dokploy alerts (digest)",
        "type": "email",
        "controlValues": {
          "skip": { "!=": [{ "var": "payload.event" }, "dokploy.restart"] },
          "subject": "Dokploy: {% if steps.digest-step.eventCount == 1 %}1 alert{% else %}{{steps.digest-step.eventCount}} alerts{% endif %}",
          "editorType": "html",
          "body": "<ul>{% for e in steps.digest-step.events %}<li><b>{{e.payload.event}}</b> {{e.payload.projectName}} {{e.payload.applicationName}}: {{e.payload.message | escape}} ({{e.payload.timestamp}}){% if e.payload.link %} <a href=\"{{e.payload.link}}\">open</a>{% endif %}</li>{% endfor %}</ul>"
        }
      }
    ]
  }'

The digest step's ID is generated from its name, which is why it is named Digest step: the templates reference it as steps.digest-step. After creating the workflow, open it in the dashboard editor and confirm the digest step's ID matches the one the email body uses.

If you run the curl again for a workflow ID that already exists, the API returns 201 with a new key such as dokploy-alerts-digest-<shortId> instead of replacing the old one, and Dokploy keeps triggering the old workflow. Check the workflowId in the response and use it as Dokploy's workflow key, or delete the duplicate.

Change the window by editing amount and unit. Longer windows mean fewer emails and later ones. How windows work is covered in the digest notifications guide.

Restart alerts skip the digest entirely, so they are never merged into a summary. The other ten types, including failures such as build.error, wait out the window. If you also want failures immediate, change all three rules together: the first step's rule becomes {"in": [{"var": "payload.event"}, ["dokploy.restart", "build.error"]]}, and the digest and digest email rules become {"!": {"in": [{"var": "payload.event"}, ["dokploy.restart", "build.error"]]}}. This variant is unverified.

Event types and payload fields

Dokploy builds the payload per alert type. Every payload carries event, status, timestamp (an ISO 8601 string) and message. The last column shows the rest.

Dokploy alertpayload.eventstatusExtra fields
Build failedbuild.errorerrorprojectName, applicationName, link
Build succeededbuild.successsuccessprojectName, applicationName, link
Database backupdatabase.backupsuccess or errorprojectName, applicationName
Docker cleanupdocker.cleanupsuccessnone
Dokploy instance backupdokploy.backupsuccess or errornone
Dokploy restartdokploy.restartsuccessnone
Domain verification faileddomain.verification_failederrorprojectName, applicationName, host, link
Scheduled job failedschedule.failureerrorprojectName, applicationName (the job name), link
Server thresholdserver.thresholdalertserverName
Snapvisor changessnapvisor.changeschanges-detectedprojectName, applicationName, environmentName, branch, commit, changedSnapshots, link
Volume backupvolume.backupsuccess or errorprojectName, applicationName

These fields follow Dokploy's own source, so check them against your Dokploy version if an email renders a blank. Optional fields that an alert does not carry render as empty in the template.

Test with a burst

Run these against a Development environment, with a test subscriber you own. Replace <subscriber-id> with it.

for i in 1 2 3 4 5 6 7 8 9 10; do
  curl -sS -X POST https://api.notifly.io/v1/events/trigger \
    -H "Authorization: ApiKey $NOTIFLY_SECRET_KEY" \
    -H "Content-Type: application/json" \
    -d "{
      \"name\": \"dokploy-alerts-digest\",
      \"to\": \"<subscriber-id>\",
      \"payload\": {
        \"event\": \"build.error\",
        \"projectName\": \"demo\",
        \"applicationName\": \"web-$i\",
        \"status\": \"error\",
        \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
        \"message\": \"Build failed for web-$i\"
      }
    }" > /dev/null
done

Expected result: after the 5 minute window, one email listing 10 alerts. The activity feed shows the first trigger as the digest and the other nine as merged into it. Send one more trigger with "event": "dokploy.restart" and it arrives straight away, without waiting for the window.

Verification status

Checked on 2026-10-30 on a Notifly dev stack, using the burst test above: 10 build.error events became one digest after the 5 minute window, and dokploy.restart skipped the digest and ran its own step immediately. The digest email subject and body also rendered with the digest events.

TODO: end-to-end verification is not complete. Not yet checked: real email delivery (the dev stack had no email provider), a live Dokploy channel posting into this workflow, and the "failures immediate" variant above. Until then, treat the workflow as a starting point.

On this page