ddup periodically checks the health of your services and updates the DNS records pointing to healthy deployments.
You can use ddup to configure round-robin DNS for load balancing and failover, for internal or external apps, automatically excluding un-healthy replicas.
ddup is not a DNS server, instead it works with "dynamic" DNS servers. Currently, it supports these DNS providers:
- Azure DNS
- Cloudflare DNS
- OVH
You can run ddup as a Docker/Podman container. Container images are available for Linux and support amd64, arm64, and armv7/armhf.
First, create a folder where you will store the configuration file config.yaml, for example $HOME/.ddup. You can then start ddup with:
# For podman, replace "docker run" with "podman run"
docker run \
-d \
--read-only \
-v $HOME/.ddup:/etc/ddup:ro \
ghcr.io/italypaleale/ddup:v0ddup follows semver for versioning. The command above uses the latest version in the 0.x branch. We do not publish a container image tagged "latest".
This is an example of a docker-compose.yaml for running ddup:
version: "3.6"
services:
ddup:
image: "ghcr.io/italypaleale/ddup:v0"
volumes:
# Set the path on the host OS
- "/path/to/ddup:/etc/ddup:ro"
restart: "unless-stopped"
read_only: true
logging:
driver: "json-file"
options:
max-file: "5"
max-size: "20m"You can download the latest version of ddup from the Releases page. Fetch the correct archive for your system and architecture, then extract the files and copy the ddup binary to /usr/local/bin or another folder.
Place the configuration for ddup in the /etc/ddup folder.
You will need to start ddup as a service using the process manager for your system.
For example, for Linux distributions based on systemd you can use the sample unit in ddup.service: copy this file to /etc/systemd/system/ddup.service.
Start the service and enable it at boot with:
sudo systemctl enable --now ddupddup requires a configuration file config.yaml in one of the following paths:
/etc/ddup/config.yaml$HOME/.ddup/config.yaml- Or in the same folder where the ddup binary is located
You can specify a custom configuration file using the
DDUP_CONFIGenvironmental variable.
You can find an example of the configuration file, and a description of every option, in the config.sample.yaml file.
interval: How often to perform health checks (e.g., "30s", "1m", "5m")
domains: Array of domains to managerecordName: The DNS record to update (e.g., "api.example.com")provider: Name of the DNS provider (from theprovidersmap)ttl: Time to live for DNS records. A short value is preferred to ensure faster failover from failed deployments. The default value is 120 (seconds, equivalent to 2 minutes)healthChecks: Configuration for health checkstimeout: Request timeout (default: "3s")attempts: Maximum number of consecutive attempts before considering the endpoint unhealthy (default: 2)
endpoints: Array of endpoints for this domainname: Friendly name for the endpoint, used for logging (optional)url: HTTP URL to check for health statusip: The IPv4 or IPv6 address to include in DNS records when healthy. IPv4 addresses create A records and IPv6 addresses create AAAA recordshost: Optional hostname to include in the requests, when the request is made to an IP address or to a hostname different from the desired one
providers: Map of providers.- Key: provider name (e.g.
my-provider-1) - Value: an object containing a provider configuration, which is one (and only one) of:
- Key: provider name (e.g.
Required settings:
subscriptionId: ID of the Azure subscription where the DNS Zone is deployedresourceGroupName: Name of the Resource Group containing the DNS Zone resourcezoneName: Name of the DNS Zone, which corresponds to the domain name (e.g.example.com)
The other settings depend on the authentication method:
- The default authentication method automatically attempts a number of supported methods, including Managed Identity, Workload Identity, Azure CLI credentials (in development), etc. You can also configure it with environmental variables including
AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_CLIENT_SECRET(full reference) - To use a service principal (with client ID and client secret), set these options:
clientId: Client IDclientSecret: Client SecrettenantId: Tenant ID
- To use a user-assigned managed identity, set:
managedIdentityClientId: Client ID of the user-assigned managed identity
Regardless of the authentication method, ensure that the principal (user, service principal, or managed identity) has the DNS Zone Contributor role assigned to the DNS zone.
If using a custom RBAC role instead, ensure it includes permissions for both IPv4 and IPv6 DNS records:
Microsoft.Network/dnsZones/A/read
Microsoft.Network/dnsZones/A/write
Microsoft.Network/dnsZones/A/delete
Microsoft.Network/dnsZones/AAAA/read
Microsoft.Network/dnsZones/AAAA/write
Microsoft.Network/dnsZones/AAAA/delete
Using the Azure CLI, the built-in DNS Zone Contributor role can be assigned with:
az role assignment create \
--assignee <client-id> \
--role "DNS Zone Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/<rg-name>/providers/Microsoft.Network/dnsZones/<zone-name>"Example:
providers:
example-provider-1:
azure:
subscriptionId: "00000000-0000-0000-0000-000000000000"
resourceGroupName: "my-dns-rg"
zoneName: "example.com"Required settings:
zoneId: Cloudflare Zone ID for your domainapiToken: Cloudflare API token with Zone:Edit permissions
To get the credentials:
- API Token: Go to Cloudflare dashboard → My Profile → API Tokens → Create Token
- Grant
Zone:Editpermissions for your domain
- Grant
- Zone ID: Found in the domain overview page
Example:
providers:
example-provider-1:
cloudflare:
apiToken: "your-cloudflare-api-token"
zoneId: "your-zone-id"Required settings:
apiKey: API keyapiSecret: API secretconsumerKey: Consumer keyzoneName: Name of the zone (e.g.example.com)
Optional settings:
endpoint: OVH API endpoint, which is one of:"eu"(default value if omitted)"ca""us"- A custom URL
To get the required credentials, navigate to this URL, replacing {zoneName} with the name of your zone (e.g. example.com):
https://api.ovh.com/createToken/index.cgi?GET=/domain/zone/{zoneName}/*&POST=/domain/zone/{zoneName}/*&DELETE=/domain/zone/{zoneName}/*
Example:
providers:
ovh-eu-example:
ovh:
apiKey: "your-ovh-api-key"
apiSecret: "your-ovh-api-secret"
consumerKey: "your-ovh-consumer-key"
zoneName: "example.com"
endpoint: "eu"enabled: Enable the server (disabled by default)bind: Address to bind to (defaults to127.0.0.1)port: Port to listen on (defaults to7401)
log: Logging optionslevel: Controls log level and verbosity. Supported values:debug,info(default),warn,error.json: If true, emits logs formatted as JSON, otherwise uses a text-based structured log format. Defaults to false if a TTY is attached (e.g. when running the binary directly in the terminal or in development); true otherwise.
