DDNS Updater
Deploy ddns-updater on Kubernetes. Keeps DNS A/AAAA records updated across 50+ providers (Cloudflare, Route53, DuckDNS, Namecheap, GoDaddy, Hetzner, and more) with a responsive web dashboard and persistent update history.
Inline config.settings entries (with token, password, or API keys) are stored in a Kubernetes Secret created by
the chart. However, they are visible in Helm release history via helm get values. For production, pre-create a
Secret containing a config.json file and reference it with config.existingSecret.
Key Features
- 50+ DNS providers — Cloudflare, Route53, DuckDNS, Namecheap, GoDaddy, Hetzner, and more
- Web UI — responsive dashboard at port 8000 for monitoring update status and history
- Multi-record support — manage records from different providers in a single deployment
- Persistent history —
updates.jsonstored in a PVC survives pod restarts - existingSecret — bring your own
config.jsonSecret for GitOps and production use - Configurable IP detection — HTTP, DNS, or combined public IP fetching strategies
- Restricted runtime — non-root container, read-only root filesystem, dropped capabilities, and ServiceAccount token automount disabled
Installation
HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install ddns-updater helmforge/ddns-updater -f values.yaml
OCI registry:
helm install ddns-updater oci://ghcr.io/helmforgedev/helm/ddns-updater -f values.yaml
Deployment Examples
# values.yaml — Single Cloudflare record
# Tokens are stored in a chart-managed Secret — consider existingSecret for production.
config:
settings:
- provider: cloudflare
zone_identifier: 'your-zone-id'
domain: 'example.com'
host: '@' # '@' for root domain, or subdomain like 'home'
ttl: 300
token: 'your-cloudflare-api-token'
proxied: false
ip_version: ipv4
ddns:
period: 5m
persistence:
enabled: true
size: 256Mi# values.yaml — Multiple records across different providers
config:
settings:
- provider: cloudflare
zone_identifier: 'cf-zone-id'
domain: 'example.com'
host: '@'
token: 'cf-api-token'
proxied: true
- provider: duckdns
domain: 'myhost.duckdns.org'
token: 'duckdns-token'
- provider: namecheap
domain: 'example.org'
host: 'home'
password: 'namecheap-ddns-password'
ddns:
period: 5m
updateCooldownPeriod: 5m # prevents flapping when IP changes rapidly
persistence:
enabled: true
size: 256Mi# values.yaml — GitOps-safe: load config.json from a pre-existing Secret
# Create the secret manually:
# kubectl create secret generic ddns-config \
# --from-file=config.json=./config.json
#
# config.json format:
# {
# "settings": [
# {
# "provider": "cloudflare",
# "zone_identifier": "zone-id",
# "domain": "example.com",
# "host": "@",
# "token": "api-token",
# "proxied": false,
# "ip_version": "ipv4"
# }
# ]
# }
config:
existingSecret: ddns-config
existingSecretKey: config.json
ddns:
period: 5m
persistence:
enabled: true
size: 256Mi# values.yaml — Web UI exposed via Ingress with TLS
# The web UI runs on port 8000 in the container (mapped to 80 on the Service).
config:
existingSecret: ddns-config
existingSecretKey: config.json
ddns:
period: 5m
rootUrl: / # set to '/ddns' if behind a reverse proxy on a subpath
persistence:
enabled: true
size: 256Mi
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: ddns.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: ddns-tls
hosts:
- ddns.example.comConfiguration Reference
Core
| Parameter | Type | Default | Description |
|---|---|---|---|
nameOverride |
string | "" |
Override the chart name. |
fullnameOverride |
string | "" |
Override the full release name. |
commonLabels |
object | {} |
Extra labels added to all resources. |
Image
| Parameter | Type | Default | Description |
|---|---|---|---|
image.repository |
string | docker.io/qmcgaw/ddns-updater |
DDNS Updater container image. |
image.tag |
string | "2.10.0" |
Image tag. |
image.pullPolicy |
string | IfNotPresent |
Image pull policy. |
imagePullSecrets |
array | [] |
Pull secrets for private registries. |
DDNS Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
config.settings |
array | [] |
Inline DNS records to update. Each entry requires provider-specific fields. |
config.existingSecret |
string | "" |
Existing Secret containing a config.json file. |
config.existingSecretKey |
string | config.json |
Key inside the existing secret for the config file. |
The config.existingSecret must contain a JSON file. The config.settings entries in values.yaml are YAML but are
converted to JSON by the chart when creating the internal Secret. When using existingSecret, create a valid JSON
file following the ddns-updater settings
format.
Application Options
| Parameter | Type | Default | Description |
|---|---|---|---|
ddns.period |
string | 5m |
Interval between IP checks and DNS updates. |
ddns.httpTimeout |
string | 10s |
HTTP timeout for IP detection and DNS provider API calls. |
ddns.publicIpFetchers |
string | all |
Public IP detection method: all, http, or dns. |
ddns.updateCooldownPeriod |
string | 5m |
Minimum time between updates per record. Prevents flapping on rapid IP change. |
ddns.logLevel |
string | info |
Log verbosity: debug, info, warning, or error. |
ddns.port |
integer | 8000 |
Web UI container listen port. |
ddns.rootUrl |
string | / |
Root URL path for the web UI. Change when hosting behind a reverse proxy at a subpath. |
ddns.extraEnv |
array | [] |
Extra environment variables for the container. |
Persistence
| Parameter | Type | Default | Description |
|---|---|---|---|
persistence.enabled |
boolean | true |
Enable PVC for updates.json (update history and status). |
persistence.size |
string | 256Mi |
PVC size. Tiny — update history is a small JSON file. |
persistence.storageClass |
string | "" |
StorageClass for the PVC. |
persistence.accessModes |
array | [ReadWriteOnce] |
PVC access modes. |
persistence.existingClaim |
string | "" |
Use an existing PVC. |
Service
| Parameter | Type | Default | Description |
|---|---|---|---|
service.type |
string | ClusterIP |
Kubernetes service type. |
service.port |
integer | 80 |
Service port (maps to container 8000). |
service.annotations |
object | {} |
Annotations for the Service. |
Ingress
| Parameter | Type | Default | Description |
|---|---|---|---|
ingress.enabled |
boolean | false |
Enable an Ingress resource for the web UI. |
ingress.ingressClassName |
string | traefik |
Ingress class name. |
ingress.annotations |
object | {} |
Annotations for the Ingress (e.g. cert-manager). |
ingress.hosts |
array | [] |
Ingress host and path rules. |
ingress.tls |
array | [] |
TLS configuration. |
Probes
| Parameter | Type | Default | Description |
|---|---|---|---|
probes.startup.enabled |
boolean | true |
Enable startup probe. |
probes.startup.initialDelaySeconds |
integer | 5 |
Startup probe initial delay. |
probes.startup.periodSeconds |
integer | 5 |
Startup probe period. |
probes.startup.failureThreshold |
integer | 12 |
Startup probe failure threshold. |
probes.liveness.enabled |
boolean | true |
Enable liveness probe. |
probes.liveness.periodSeconds |
integer | 15 |
Liveness probe period. |
probes.liveness.failureThreshold |
integer | 3 |
Liveness probe failure threshold. |
probes.readiness.enabled |
boolean | true |
Enable readiness probe. |
probes.readiness.periodSeconds |
integer | 10 |
Readiness probe period. |
probes.readiness.failureThreshold |
integer | 3 |
Readiness probe failure threshold. |
Resources and Security
| Parameter | Type | Default | Description |
|---|---|---|---|
resources.requests.cpu |
string | 10m |
Default CPU request. |
resources.requests.memory |
string | 32Mi |
Default memory request. |
resources.limits.cpu |
string | 100m |
Default CPU limit. |
resources.limits.memory |
string | 128Mi |
Default memory limit. |
podSecurityContext |
object | non-root seccomp defaults | Pod-level security context. |
securityContext |
object | non-root read-only defaults | Container-level security context. |
Service Account
| Parameter | Type | Default | Description |
|---|---|---|---|
serviceAccount.create |
boolean | false |
Create a dedicated ServiceAccount. |
serviceAccount.automountServiceAccountToken |
boolean | false |
Mount Kubernetes API token into pods. |
serviceAccount.name |
string | "" |
Override the ServiceAccount name. |
serviceAccount.annotations |
object | {} |
Annotations for the ServiceAccount. |
Scheduling
| Parameter | Type | Default | Description |
|---|---|---|---|
nodeSelector |
object | {} |
Node selector for scheduling. |
tolerations |
array | [] |
Tolerations for scheduling. |
affinity |
object | {} |
Affinity rules. |
topologySpreadConstraints |
array | [] |
Topology spread constraints. |
priorityClassName |
string | "" |
PriorityClass for the pod. |
terminationGracePeriodSeconds |
integer | 30 |
Termination grace period. |
podLabels |
object | {} |
Extra labels for the pod. |
podAnnotations |
object | {} |
Extra annotations for the pod. |
Extra
| Parameter | Type | Default | Description |
|---|---|---|---|
extraVolumes |
array | [] |
Extra volumes to attach to the pod. |
extraVolumeMounts |
array | [] |
Extra volume mounts for the container. |
extraManifests |
array | [] |
Extra Kubernetes manifests deployed alongside the chart. |