DaemonSets and Jobs
Not every workload fits the Deployment or StatefulSet model. DaemonSets ensure a Pod runs on every node (or a subset of nodes), while Jobs and CronJobs handle one-off and scheduled tasks that run to completion.
DaemonSets
What is a DaemonSet?
A DaemonSet ensures that all (or some) nodes run a copy of a Pod. As nodes are added to the cluster, Pods are automatically added to them. As nodes are removed, Pods are garbage collected.
Common use cases:
- Log collection (Fluentd, Filebeat)
- Node monitoring (Prometheus Node Exporter, Datadog agent)
- Cluster storage (Ceph, GlusterFS)
- Network plugins (Calico, Cilium)
- Security agents (Falco)
DaemonSet YAML
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
labels:
app: fluentd
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
containers:
- name: fluentd
image: fluentd:v1.16
resources:
limits:
memory: "200Mi"
cpu: "100m"
requests:
memory: "100Mi"
cpu: "50m"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers
kubectl apply -f daemonset.yaml
# One Pod per node
kubectl get pods -l app=fluentd -o wide
Target Specific Nodes
Use nodeSelector or affinity to run on specific nodes:
spec:
template:
spec:
nodeSelector:
disk: ssd # Only nodes with label disk=ssd
# Or with tolerations for control plane nodes
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
DaemonSet Update Strategies
| Strategy | Behavior |
|---|---|
| RollingUpdate (default) | Updates Pods one node at a time |
| OnDelete | Only updates when you manually delete old Pods |
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # Update 1 node at a time
DaemonSet Commands
kubectl get daemonsets # List DaemonSets
kubectl describe ds fluentd # Detailed info
kubectl rollout status ds fluentd # Rollout status
kubectl delete ds fluentd # Delete
Jobs
What is a Job?
A Job creates one or more Pods and ensures they run to completion. Unlike Deployments (which keep Pods running forever), Jobs exit when the work is done.
Use cases:
- Database migrations
- Batch processing
- Data import/export
- Sending bulk emails
- Report generation
Basic Job
apiVersion: batch/v1
kind: Job
metadata:
name: pi-calculator
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never # Required: Never or OnFailure
backoffLimit: 4 # Retry up to 4 times on failure
kubectl apply -f job.yaml
# Watch the Job
kubectl get jobs
kubectl get pods
# View the output
kubectl logs job/pi-calculator
Output:
NAME COMPLETIONS DURATION AGE
pi-calculator 1/1 15s 20s
Parallel Jobs
Run multiple Pods in parallel:
apiVersion: batch/v1
kind: Job
metadata:
name: batch-process
spec:
completions: 10 # Total tasks to complete
parallelism: 3 # Run 3 Pods at a time
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "echo Processing item && sleep 5"]
restartPolicy: Never
This runs 3 Pods in parallel until all 10 completions are done.
Job Completion Modes
| Field | Description |
|---|---|
completions |
Number of successful completions needed |
parallelism |
Max Pods running at the same time |
backoffLimit |
Max retries before marking Job as failed (default: 6) |
activeDeadlineSeconds |
Max time the Job can run |
ttlSecondsAfterFinished |
Auto-delete completed Job after N seconds |
Auto-Cleanup Completed Jobs
spec:
ttlSecondsAfterFinished: 300 # Delete Job 5 minutes after completion
Job Failure Handling
spec:
backoffLimit: 4 # Retry up to 4 times
activeDeadlineSeconds: 600 # Timeout after 10 minutes
template:
spec:
restartPolicy: OnFailure # Restart container in same Pod on failure
restartPolicy |
Behavior |
|---|---|
Never |
Create a new Pod for each retry (old Pods preserved for debugging) |
OnFailure |
Restart the container within the same Pod |
CronJobs
What is a CronJob?
A CronJob creates Jobs on a schedule — like Unix cron. Perfect for periodic tasks.
CronJob YAML
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # Every day at 2:00 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: my-backup-tool:1.0
command: ["/backup.sh"]
env:
- name: BACKUP_TARGET
value: "s3://my-bucket/backups"
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3 # Keep last 3 successful Jobs
failedJobsHistoryLimit: 1 # Keep last 1 failed Job
concurrencyPolicy: Forbid # Don't start new if previous is running
Cron Schedule Syntax
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6, Sunday = 0)
│ │ │ │ │
* * * * *
Common schedules:
| Schedule | Meaning |
|---|---|
*/5 * * * * |
Every 5 minutes |
0 * * * * |
Every hour |
0 2 * * * |
Daily at 2:00 AM |
0 0 * * 0 |
Weekly on Sunday at midnight |
0 0 1 * * |
Monthly on the 1st at midnight |
Concurrency Policies
| Policy | Behavior |
|---|---|
| Allow (default) | Allows concurrent Jobs |
| Forbid | Skips new Job if previous is still running |
| Replace | Cancels running Job and starts a new one |
CronJob Commands
kubectl get cronjobs # List CronJobs
kubectl describe cronjob daily-backup
kubectl get jobs --watch # Watch Jobs created by CronJob
# Manually trigger a CronJob
kubectl create job --from=cronjob/daily-backup manual-backup
Hands-On Example
Let’s create a DaemonSet, a Job, and a CronJob in Minikube:
# daemonset-monitor.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-monitor
spec:
selector:
matchLabels:
app: node-monitor
template:
metadata:
labels:
app: node-monitor
spec:
containers:
- name: monitor
image: busybox
command: ["sh", "-c", "while true; do echo $(hostname) - $(date) - Mem: $(cat /proc/meminfo | head -1); sleep 30; done"]
resources:
limits:
cpu: "50m"
memory: "32Mi"
---
# job-hello.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: hello-job
spec:
completions: 3
parallelism: 2
ttlSecondsAfterFinished: 120
template:
spec:
containers:
- name: hello
image: busybox
command: ["sh", "-c", "echo Hello from $(hostname) at $(date) && sleep 3"]
restartPolicy: Never
---
# cronjob-report.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: minutely-report
spec:
schedule: "*/1 * * * *"
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: reporter
image: busybox
command: ["sh", "-c", "echo Report generated at $(date)"]
restartPolicy: Never
kubectl apply -f daemonset-monitor.yaml
kubectl apply -f job-hello.yaml
kubectl apply -f cronjob-report.yaml
# Watch everything
kubectl get ds,jobs,cronjobs
kubectl get pods -w
Summary
| Controller | Purpose | Runs Until |
|---|---|---|
| Deployment | Stateless apps | Forever (keeps running) |
| StatefulSet | Stateful apps | Forever (keeps running) |
| DaemonSet | One Pod per node | Forever (per node) |
| Job | One-off tasks | Completion |
| CronJob | Scheduled tasks | Completion (repeatedly) |
What’s Next?
Now let’s learn about Ingress — the Kubernetes resource that provides HTTP/HTTPS routing to your Services, enabling you to expose multiple services through a single entry point with path-based and host-based routing.