Skip to content

Cloudflare DDNS

Holden can automatically create and update Cloudflare DNS records for your apps. When a container with a domain: starts, its domain gets pointed to your server’s public IP — no manual DNS management needed.

This is handled by a lightweight sidecar container (holden-ddns) that watches Docker labels and syncs them to Cloudflare.

  1. The sidecar reads holden.domains labels from all running containers via the Docker socket
  2. It detects your server’s public IP
  3. For each domain, it finds the matching Cloudflare zone and creates or updates an A record
  4. It reacts to container start/stop events and periodically checks for IP changes
flowchart LR
    D[Docker socket] -->|read labels| S[holden-ddns]
    S -->|detect IP| IP[Public IP]
    S -->|update records| CF[Cloudflare API]

Set CLOUDFLARE_API_TOKEN on the Holden container. When present, Holden spawns the holden-ddns sidecar automatically. Remove the variable to disable DDNS.

  1. Go to Cloudflare API Tokens
  2. Click Create Token
  3. Use the Edit zone DNS template, or create a custom token with:
    • Zone > Zone > Read — to list your zones
    • Zone > DNS > Edit — to create and update records
  4. Set the zone scope to All zones (or limit to specific zones)

Holden sets a holden.domains label on every container that has a domain: configured:

holden.domains: "app.example.com,www.example.com"

This is a comma-separated list of all domains for that service. The DDNS sidecar reads this label — it never parses Traefik rules or reads your holden.yml files.

The Overseer also sets holden.domains on the Holden container itself when HOLDEN_PUBLIC_DOMAIN is configured, so the dashboard/webhook domain gets DNS records too.

The sidecar adds a comment to every DNS record it creates:

managed by holden
Record exists?CommentFORCE offFORCE on
NoCreates recordCreates record
Yes, wrong IPmanaged by holdenUpdates recordUpdates record
Yes, wrong IPOther / noneSkipsUpdates record
Yes, correct IPNo-opNo-op

Without FORCE, the sidecar only touches records it created. This prevents accidentally overwriting records managed by other tools or set manually.

When a domain is removed from your config (or an app is deleted), the DNS record is not automatically deleted or modified. It continues pointing at your server’s IP, where Traefik will return a 404. Clean up stale records manually in the Cloudflare dashboard when you’re ready.

The sidecar subscribes to Docker container events (start, stop, die). When a Holden-managed container changes, it re-scans all labels and syncs. This means DNS updates happen within seconds of a deployment.

A periodic fallback (every 5 minutes) catches IP changes that happen without container events — for example, if your ISP assigns a new IP.