Skip to content

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.

config.settings embeds credentials in Helm values — prefer existingSecret

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 historyupdates.json stored in a PVC survives pod restarts
  • existingSecret — bring your own config.json Secret for GitOps and production use
  • Configurable IP detection — HTTP, DNS, or combined public IP fetching strategies

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.com

Configuration Reference

Core

ParameterTypeDefaultDescription
nameOverridestring""Override the chart name.
fullnameOverridestring""Override the full release name.
commonLabelsobject{}Extra labels added to all resources.

Image

ParameterTypeDefaultDescription
image.repositorystringdocker.io/qmcgaw/ddns-updaterDDNS Updater container image.
image.tagstring"v2.9.0"Image tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

DDNS Configuration

ParameterTypeDefaultDescription
config.settingsarray[]Inline DNS records to update. Each entry requires provider-specific fields.
config.existingSecretstring""Existing Secret containing a config.json file.
config.existingSecretKeystringconfig.jsonKey inside the existing secret for the config file.
config.json is JSON, not YAML

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

ParameterTypeDefaultDescription
ddns.periodstring5mInterval between IP checks and DNS updates.
ddns.httpTimeoutstring10sHTTP timeout for IP detection and DNS provider API calls.
ddns.publicIpFetchersstringallPublic IP detection method: all, http, or dns.
ddns.updateCooldownPeriodstring5mMinimum time between updates per record. Prevents flapping on rapid IP change.
ddns.logLevelstringinfoLog verbosity: debug, info, warning, or error.
ddns.portinteger8000Web UI container listen port.
ddns.rootUrlstring/Root URL path for the web UI. Change when hosting behind a reverse proxy at a subpath.
ddns.extraEnvarray[]Extra environment variables for the container.

Persistence

ParameterTypeDefaultDescription
persistence.enabledbooleantrueEnable PVC for updates.json (update history and status).
persistence.sizestring256MiPVC size. Tiny — update history is a small JSON file.
persistence.storageClassstring""StorageClass for the PVC.
persistence.accessModesarray[ReadWriteOnce]PVC access modes.
persistence.existingClaimstring""Use an existing PVC.

Service

ParameterTypeDefaultDescription
service.typestringClusterIPKubernetes service type.
service.portinteger80Service port (maps to container 8000).
service.annotationsobject{}Annotations for the Service.

Ingress

ParameterTypeDefaultDescription
ingress.enabledbooleanfalseEnable an Ingress resource for the web UI.
ingress.ingressClassNamestringtraefikIngress class name.
ingress.annotationsobject{}Annotations for the Ingress (e.g. cert-manager).
ingress.hostsarray[]Ingress host and path rules.
ingress.tlsarray[]TLS configuration.

Probes

ParameterTypeDefaultDescription
probes.startup.enabledbooleantrueEnable startup probe.
probes.startup.initialDelaySecondsinteger5Startup probe initial delay.
probes.startup.periodSecondsinteger5Startup probe period.
probes.startup.failureThresholdinteger12Startup probe failure threshold.
probes.liveness.enabledbooleantrueEnable liveness probe.
probes.liveness.periodSecondsinteger15Liveness probe period.
probes.liveness.failureThresholdinteger3Liveness probe failure threshold.
probes.readiness.enabledbooleantrueEnable readiness probe.
probes.readiness.periodSecondsinteger10Readiness probe period.
probes.readiness.failureThresholdinteger3Readiness probe failure threshold.

Resources and Security

ParameterTypeDefaultDescription
resourcesobject{}CPU and memory requests and limits.
podSecurityContextobject{}Pod-level security context.
securityContextobject{}Container-level security context.

Scheduling

ParameterTypeDefaultDescription
nodeSelectorobject{}Node selector for scheduling.
tolerationsarray[]Tolerations for scheduling.
affinityobject{}Affinity rules.
topologySpreadConstraintsarray[]Topology spread constraints.
priorityClassNamestring""PriorityClass for the pod.
terminationGracePeriodSecondsinteger30Termination grace period.
podLabelsobject{}Extra labels for the pod.
podAnnotationsobject{}Extra annotations for the pod.

Extra

ParameterTypeDefaultDescription
extraVolumesarray[]Extra volumes to attach to the pod.
extraVolumeMountsarray[]Extra volume mounts for the container.
extraManifestsarray[]Extra Kubernetes manifests deployed alongside the chart.

More Information