Running Cloudflare DDNS as a Kubernetes CronJob

Share
Running Cloudflare DDNS as a Kubernetes CronJob

This is how I run Cloudflare DDNS as a Kubernetes CronJob on my home cluster, with deployment managed by ArgoCD. This way, I can control how often it runs and trust ArgoCD to keep it healthy.


Create the Helm Chart

First, create a folder for cloudflare-ddns and scaffold a Helm chart:

# Create a directory
mkdir cloudflare-ddns
cd cloudflare-ddns

# Create a Helm chart
helm create cloudflare-ddns
cd cloudflare-ddns

Helm will generate a lot of boilerplate. You can clean it up by removing unnecessary files. Keep only:

  • Chart.yaml
  • values.yaml
  • the templates folder (but clear out its sample manifests — we’ll add our own).

Edit values.yaml

Open values.yaml and add your configuration:

cloudflare:
  zoneID: "<your Cloudflare zone ID>"
  recordName: "<your DNS A record name, e.g. katam.cc>"
  ttl: <Integer: TTL in seconds (1 means 'auto'). Must be 60–86400 (or 30 for Enterprise).>
  proxied: <Boolean: true or false — whether Cloudflare proxying is enabled.>

Note: Use quotation marks for strings.

Example:

cloudflare:
  zoneID: "abc1234567890"
  recordName: "katam.cc"
  ttl: 300
  proxied: true

Create the CronJob manifest

In templates/, create a file named cronjob.yaml and paste this:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cloudflare-ddns-updater
spec:
  schedule: "*/5 * * * *"  # Runs every 5 minutes
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 1
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: cloudflare-ddns-updater
              image: ghcr.io/prateekatam/cloudflare-ddns:1.0.4
              env:
                - name: CLOUDFLARE_ZONE_ID
                  value: "{{ .Values.cloudflare.zoneID }}"
                - name: CLOUDFLARE_RECORD_NAME
                  value: "{{ .Values.cloudflare.recordName }}"
                - name: CLOUDFLARE_API_TOKEN
                  valueFrom:
                    secretKeyRef:
                      name: cloudflare-api-key
                      key: CLOUDFLARE_API_TOKEN  # Must match your secret key name
                - name: CLOUDFLARE_TTL
                  value: "{{ .Values.cloudflare.ttl }}"
                - name: CLOUDFLARE_PROXIED
                  value: "{{ .Values.cloudflare.proxied }}"
          restartPolicy: OnFailure

You must create a Kubernetes Secret in the same namespace, named cloudflare-api-key.
It should contain your Cloudflare API token with the key CLOUDFLARE_API_TOKEN.

Example:

kubectl create secret generic cloudflare-api-key \
  --from-literal=CLOUDFLARE_API_TOKEN="your_cloudflare_api_token" \
  -n cloudflare

Deploy with ArgoCD

Create an app.yaml for ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cloudflare-ddns-updater
  namespace: argocd
spec:
  project: default
  sources:
    - repoURL: "<your Git repository URL>"
      path: cloudflare-ddns
      targetRevision: HEAD
  destination:
    server: https://kubernetes.default.svc
    namespace: cloudflare
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Replace <your Git repository URL> with your repo.


Apply & Verify

Apply the ArgoCD application and watch it sync and create your CronJob:

kubectl apply -f app.yaml

ArgoCD will handle the deployment and ensure the CronJob runs every 5 minutes.


That’s it! Now your external IP updates automatically — no manual changes, no downtime when you’re away.