A CronJob creates a Job from its jobTemplate on a cron schedule of minute, hour, day of month, month and weekday: 30 2 * * * is 02:30 daily, */15 9-17 * * 1-5 every quarter hour in weekday office hours. timeZone (stable since 1.27) takes an IANA name. Database Backups's backup, run every minute to watch it:
kubectl apply -f - >/dev/null <<'EOF'
apiVersion: batch/v1
kind: CronJob
metadata: { name: db-backup }
spec:
schedule: "* * * * *" # nightly in real life: "30 2 * * *"
timeZone: Asia/Kuala_Lumpur
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 2
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: dump
image: localhost:33500/postgres:18
command: [sh, -c, 'echo "$(date +%T) dump: $(pg_dump | gzip | wc -c) bytes"']
envFrom: [{ configMapRef: { name: api-config } }]
env:
- name: PGUSER
valueFrom: { secretKeyRef: { name: db-credentials, key: username } }
- name: PGPASSWORD
valueFrom: { secretKeyRef: { name: db-credentials, key: password } }
EOF
until [ "$(kubectl get jobs -o name | grep -c db-backup)" -ge 2 ]; do sleep 5; done
kubectl wait --for=condition=Complete $(kubectl get jobs -o name | grep db-backup) >/dev/null
kubectl get jobs | grep -E '^NAME|db-backup'
for j in $(kubectl get jobs -o name | grep db-backup); do kubectl logs $j; done
kubectl delete cronjob db-backup && sleep 5 && kubectl get jobs -o name | grep -c db-backupOutput
NAME STATUS COMPLETIONS DURATION AGE db-backup-29839238 Complete 1/1 4s 65s db-backup-29839239 Complete 1/1 4s 5s 16:38:01 dump: 1239 bytes 16:39:02 dump: 1238 bytes cronjob.batch "db-backup" deleted from booknest namespace 0
Job names end in the scheduled minute since the Unix epoch, and deleting the CronJob deleted its Jobs. concurrencyPolicy handles a run still going at the next tick: Allow (default) overlaps, Forbid skips, Replace cancels the old run. A missed tick still runs within startingDeadlineSeconds. A real backup would copy the dump to object storage rather than count its bytes.