Skip to main content

dynamic-dns

Overview

dynamic-dns periodically detects your public IP address and updates Cloudflare DNS A records, keeping them synchronized with your dynamic IP. This eliminates manual DNS updates when your ISP changes your public IP address.

The Problem

Home networks and some cloud deployments have dynamic public IPs that change periodically:

  • Without automatic DNS updates, your domain's A records point to stale IPs
  • Services become unreachable after an IP change until you manually update DNS
  • For frequently changing IPs, manual updates become impractical

Why This Utility

dynamic-dns automatically keeps your DNS records in sync with your public IP by running continuously, detecting IP changes at regular intervals (default: every 10 minutes), and updating Cloudflare A records only when needed.

Unlike traditional dynamic-DNS tools (e.g., ddclient), dynamic-dns is designed to run in a restricted Kubernetes pod security context. It runs as a non-root user with minimal capabilities, no filesystem write access, and can operate securely without privileged permissions.

How It Works

Workflow

[Startup]

Detect public IP from api.ipify.org

For each configured zone and domain:
├─ Find zone ID in Cloudflare
├─ Find DNS A record
├─ Compare with detected IP
└─ Update if changed

Sleep for interval (default: 10m)

[Repeat from step 1]

Key Concepts

  • Public IP Detection: Uses the public IPIFY service (https://api.ipify.org). This works for any network, whether it's a home network, VPN, or cloud deployment.

  • Comparison-Based Updates: Only updates DNS records if the IP has changed, reducing unnecessary Cloudflare API calls.

  • Multiple Zones and Domains: Configure multiple Cloudflare zones, each with multiple domains, in a single invocation.

  • Graceful Shutdown: Responds to SIGINT and SIGTERM, allowing clean shutdown during pod termination or container orchestration.

Installation

Prerequisites

  • Kubernetes cluster
  • Cloudflare account with at least one domain/zone configured
  • Cloudflare API token with DNS write permissions
  • Helm 3.x

Deploy with Helm

Add the repository and install:

helm repo add homelab-helper https://benfiola.github.io/homelab-helper
helm repo update
helm install dynamic-dns homelab-helper/dynamic-dns \
--namespace default \
--create-namespace \
--values values.yaml

See Configuration below for values.yaml schema.

Verify Installation

First, create a Secret with your Cloudflare API token:

kubectl create secret generic dynamic-dns-credentials \
--from-literal=api-token=<your-cloudflare-api-token>

Then check that the Deployment is running:

kubectl get deployment dynamic-dns
kubectl logs -l app.kubernetes.io/name=dynamic-dns -f

Configuration

CLI Flags and Environment Variables

The utility is invoked as:

homelab-helper dynamic-dns [flags]
FlagEnv VarRequiredDefaultDescription
--cloudflare-api-tokenCLOUDFLARE_API_TOKENCloudflare API token with DNS write permissions
--cloudflare-zonesCLOUDFLARE_ZONES[]JSON array of zones and domains (see schema below)
--intervalINTERVAL10mSync interval (e.g., 10m, 1h, 30s)
--log-levelLOG_LEVELinfoLog level (debug, info, warn, error)
--log-formatLOG_FORMATtextLog format (text or json)

Cloudflare Zones Configuration

The --cloudflare-zones flag accepts a JSON array defining which zones and domains to sync:

[
{
"name": "example.com",
"domains": ["home.example.com", "vpn.example.com"]
},
{
"name": "another-domain.com",
"domains": ["app.another-domain.com"]
}
]

Schema:

  • name (string): Cloudflare zone name (typically the root domain)
  • domains (array of strings): DNS A record names to update (can be subdomains)

Helm Chart Values

Create a values.yaml to configure the chart:

config:
cloudflareApiTokenSecret: "dynamic-dns-credentials"
cloudflareApiTokenKey: "api-token"
cloudflareZones:
- name: "example.com"
domains:
- "home.example.com"
- "vpn.example.com"
interval: "10m"
logLevel: "info"

deployment:
image:
tag: "" # Defaults to chart version
resources:
null
# Example:
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 50m
# memory: 64Mi

The Secret referenced by cloudflareApiTokenSecret must be created separately in the same namespace and contain the API token at the key specified by cloudflareApiTokenKey.

Usage

Monitoring Syncs

The utility logs each sync operation. Monitor logs to verify behavior:

kubectl logs -l app.kubernetes.io/name=dynamic-dns -f

Troubleshooting

Public IP Detection Fails

Symptom: Logs show "failed to get public ip address"

Diagnose:

Verify the IPIFY service is reachable:

curl https://api.ipify.org

Should return your public IP address as plain text.

Common causes:

  • Network connectivity issue (egress blocked, DNS resolution failure)
  • IPIFY service is down (check status page)
  • Pod has no internet access (check network policies, security groups, NAT)

DNS Record Update Fails

Symptom: Logs show "failed to update dns record" or "no dns records with name"

Diagnose:

Verify the Cloudflare API token and configuration:

# Test API token validity
curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer <YOUR_API_TOKEN>"

# List zones
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer <YOUR_API_TOKEN>"

# List DNS records in a zone
curl -X GET "https://api.cloudflare.com/client/v4/zones/<ZONE_ID>/dns_records" \
-H "Authorization: Bearer <YOUR_API_TOKEN>"

Common causes:

  • API token lacks DNS write permissions (check token settings in Cloudflare dashboard)
  • Zone name doesn't exist or is misspelled
  • Domain/subdomain doesn't exist in the zone (ensure A record exists in Cloudflare)
  • Zone name and domain mismatch (domain must belong to the specified zone)

Security Considerations

API Token Protection

The Cloudflare API token grants DNS write access to your domains. Protect it carefully:

Storage:

  • Never commit tokens to version control or container images
  • Use Kubernetes Secrets or a secret management system (HashiCorp Vault, AWS Secrets Manager)
  • Rotate tokens periodically via Cloudflare dashboard

Access Control:

  • Restrict which pods and workloads can access the token Secret
  • Use RBAC and Pod Security Standards
  • Limit token permissions: create a token with DNS-only write access instead of full account access

Public IP Detection

The utility queries api.ipify.org to detect your public IP. This service:

  • Receives only your public IP address (no other private data)
  • Is maintained by Cloudflare
  • May log requests (review their privacy policy if concerned)

For maximum privacy, consider self-hosting an IP detection service or using your own infrastructure.

Zone and Domain Limits

The API token's permissions naturally prevent you from configuring zones and domains you don't control. Scope the token narrowly to only the zones it needs to manage.

See Also