ddup
GitHub

Notifications

ddup can send you a message when it changes the DNS records of a domain, when an update fails, or when a domain has no healthy endpoint. It sends them to webhooks (including Slack, Discord, or ntfy) and by email.

Notifications are off until you add a notifications section to the configuration.

Events#

EventSent when
dns_updatedddup changed the DNS records of a domain. The message lists the addresses published now and the ones that were in DNS before.
dns_update_failedUpdating the DNS records of a domain failed. The message includes the error. ddup tries again at every check, but doesn’t send the event again while the error stays the same.
all_unhealthyA domain has no healthy endpoint, so ddup leaves its DNS records as they are. The event is sent once when the domain enters this state.

When a domain recovers, ddup updates DNS as usual and sends dns_updated. If the records in DNS are already the right ones, nothing changes and no event is sent.

Each destination receives every event by default. To receive only some, list them in the destination’s events option.

Webhooks#

Each entry in notifications.webhooks sends a POST request to a URL.

OptionTypeDefaultDescription
namestringrequiredName of the destination, shown in logs. It must be unique among the webhooks.
urlstringrequiredURL to send the request to
formatstringplainFormat of the message: plain, slack, discord, or ntfy
tokenstringToken sent in the Authorization: Bearer <token> header, for example to publish to a protected ntfy topic. Some webhook formats, including Slack and Discord, don’t need it.
eventslistAll eventsEvents to send, see Events
timeoutduration20sTimeout of each request
maxAttemptsnumber3Number of attempts to send each message, including the first. Set it to 1 to turn off retries.
retryIntervalduration20sTime to wait between attempts
allowPrivateDestinationsbooleanfalseAllow sending to private addresses, see Private destinations

The formats are:

  • plain sends the message as plain text.
  • slack sends a message for a Slack incoming webhook, or for another service that accepts Slack’s format.
  • discord sends a message for a Discord webhook.
  • ntfy publishes to an ntfy topic, with a title, a priority, and tags. See Messages.

ddup retries a request after a network error, or a 408 or 5xx response.

Private destinations#

By default, ddup refuses to send webhooks to private and non-routable addresses, e.g. 10.0.0.0/8 or 192.168.0.0/16.

If the destination is on a network you trust, such as an ntfy server in your LAN or cluster, set allowPrivateDestinations: true.

Webhook examples#

Send every event to an ntfy topic on a server in your private network:

notifications:
  webhooks:
    - name: "alerts"
      format: "ntfy"
      url: "https://ntfy.example.com/ddup"
      token: !file /run/secrets/ntfy-token
      allowPrivateDestinations: true

Send failed updates and outages to a Slack channel, and every event to a Discord channel:

notifications:
  webhooks:
    - name: "slack-alerts"
      format: "slack"
      url: "https://hooks.slack.com/services/..."
      events: ["dns_update_failed", "all_unhealthy"]
    - name: "discord"
      format: "discord"
      url: "https://discord.com/api/webhooks/..."

Email#

Each entry in notifications.emails sends an email to one or more addresses.

OptionTypeDefaultDescription
namestringrequiredName of the destination, shown in logs. It must be unique among the emails.
connectionStringstringrequiredEmail provider and sender, see Connection strings
tolistrequiredAddresses to send the email to
eventslistAll eventsEvents to send, see Events

Emails are sent once, without retries.

Connection strings#

The connection string picks the email provider and sets the sender:

ProviderConnection string
Resendresend://<api-key>?fromAddress=<address>&fromName=<name>
SMTPsmtp://<username>:<password>@<host>:<port>?fromAddress=<address>&fromName=<name>&tls=<mode>
AWS SESawsses://<access-key-id>:<secret-access-key>@<region>?fromAddress=<address>&fromName=<name>
SendGridsendgrid://<api-key>?fromAddress=<address>&fromName=<name>

fromName is optional. For SMTP, tls is auto (the default), starttls, implicit, or none.

go-kit’s documentation lists all the options.

Email example#

Send failed updates and outages with Resend, and every event with an SMTP server:

notifications:
  emails:
    - name: "ops-smtp"
      # The file contains smtp://ddup:<password>@smtp.example.com:587?fromAddress=ddup@example.com&tls=starttls
      connectionString: !file /run/secrets/smtp-connection-string
      to: ["postmaster@example.com", "oncall@example.com"]
    - name: "ops-resend"
      # The file contains resend://<api-key>?fromAddress=ddup@example.com&fromName=ddup
      connectionString: !file /run/secrets/resend-connection-string
      to: ["postmaster@example.com"]
      events: ["dns_update_failed", "all_unhealthy"]

Delivery#

Each webhook and each email recipient has its own queue of up to 64 events, sent in order. If a queue is full, ddup drops the new event for that destination and logs a warning.

Messages#

Every message has a title and a body, and ddup uses them the same way for each kind of destination:

  • An email has the title as its subject, and the body as its text
  • An ntfy notification has the title as its title, and the body as its message
  • Plain text, Slack, and Discord messages contain the title, an empty line, and the body

The Status line of the body is healthy when all endpoints of the domain are healthy, warning when only some are, and unhealthy when none are or the update failed.

ntfy notifications also have tags and a priority. The first tag matches the status: green_circle for healthy, yellow_circle for warning, and red_circle for unhealthy. dns_update_failed and all_unhealthy add the warning tag. The priority is urgent (5) for all_unhealthy, high (4) for dns_update_failed, and the ntfy server’s default for dns_updated.

A dns_updated event sent to ntfy. The first two lines are the title and the tags, which ddup sends as ntfy headers, and the rest is the body:

Title: example.com now points to 192.0.2.10, 192.0.2.11
Tags: green_circle

Event: dns_updated
Domain: example.com
Time: 2026-01-02T03:04:05Z
Status: healthy
Now: 192.0.2.10, 192.0.2.11
Before: 192.0.2.12

Endpoints:
  server1 (192.0.2.10): healthy
  server2 (192.0.2.11): healthy
  server3 (192.0.2.12): healthy

A dns_update_failed event sent by email, with the subject first:

Subject: DNS update failed for example.com

Event: dns_update_failed
Domain: example.com
Time: 2026-01-02T03:04:05Z
Status: unhealthy
Wanted: 192.0.2.10, 192.0.2.11
Before: 192.0.2.12
Error: provider returned status 500

Endpoints:
  server1 (192.0.2.10): healthy
  server2 (192.0.2.11): healthy
  server3 (192.0.2.12): UNHEALTHY

An all_unhealthy event sent to Slack or Discord:

No healthy endpoints for example.com

Event: all_unhealthy
Domain: example.com
Time: 2026-01-02T03:04:05Z
Status: unhealthy
Unchanged: 192.0.2.10, 192.0.2.11

Endpoints:
  server1 (192.0.2.10): UNHEALTHY
  server2 (192.0.2.11): UNHEALTHY
Edit this page on GitHub