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]
| Flag | Env Var | Required | Default | Description |
|---|---|---|---|---|
--cloudflare-api-token | CLOUDFLARE_API_TOKEN | ✓ | Cloudflare API token with DNS write permissions | |
--cloudflare-zones | CLOUDFLARE_ZONES | [] | JSON array of zones and domains (see schema below) | |
--interval | INTERVAL | 10m | Sync interval (e.g., 10m, 1h, 30s) | |
--log-level | LOG_LEVEL | info | Log level (debug, info, warn, error) | |
--log-format | LOG_FORMAT | text | Log 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
- Cloudflare API Documentation — DNS API, authentication, token permissions
- IPIFY Documentation — Public IP detection service