Kubernetes Notes - Jobs, CronJob and Init Container

In Kubernetes, some workloads need to run continuously, such as web servers or APIs. These are typically managed by controllers like Deployments.

However, other workloads only need to run once and finish, such as scripts, data processing tasks, or backups. Kubernetes provides Jobs and CronJobs to handle these types of workloads.

Table of Contents

Job

A Job is a Kubernetes resource used to run a task until completion.

The Job controller creates a Pod that executes a command. Once the task finishes successfully, the Pod stops and the Job is marked as completed.

  • Runs a task once
  • Ensures the task completes successfully
  • Creates one or more Pods to execute the task
  • Stops once the task finishes

Create Job.

 1apiVersion: batch/v1
 2kind: Job
 3metadata:
 4  name: sleeper
 5spec:
 6  template:
 7    spec:
 8      restartPolicy: Never
 9      containers:
10      - name: sleeper
11        image: debian
12        command: ["sleep", "15"]

Get the job.

1kubectl get job -n demo
2NAME      STATUS    COMPLETIONS   DURATION   AGE
3sleeper   Running   0/1           12s        12s

Notice that job is not finish yet. After 15s the job completed.

1kubectl get job -n demo
2NAME      STATUS     COMPLETIONS   DURATION   AGE
3sleeper   Complete   1/1           29s        71s

Cron

Sometimes tasks must run periodically, such as every hour or every day.

In traditional systems, this is handled using cron. Kubernetes provides the same functionality using CronJobs.

Create Cron.

  • schedule → cron expression defining when the job runs
  • jobTemplate → specification of the Job to run

This cronjob will run every minute. Verify cronjob created.

1kubectl get cronjob -n demo
2NAME      SCHEDULE      TIMEZONE   SUSPEND   ACTIVE   LAST SCHEDULE   AGE
3sleeper   */1 * * * *   <none>     False     1        7s              2m44s

Get the pods.

1kubectl get pods -n demo
2NAME                  READY   STATUS      RESTARTS      AGE
3sleeper-29552118-sk9pc   0/1     Completed   0             2m11s
4sleeper-29552119-p88vr   0/1     Completed   0             71s

Notice that the pod is not READY, this a normal behavior - that means container has finished its job.

Init Container

You can create a container that performs task before your main container starts.

  • Runs before any app container in the Pod
  • Performs initialization tasks
  • Must complete successfully before the main containers start
  • Can run multiple init containers in sequence

Create Init Container

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: init-example
 5spec:
 6  initContainers:
 7  - name: init-myservice
 8    image: busybox
 9    command: ['sh', '-c', 'echo "Initializing..." && sleep 5 && touch /data/ready']
10    volumeMounts:
11    - name: shared-data
12      mountPath: /data
13  containers:
14  - name: app
15    image: nginx
16    volumeMounts:
17    - name: shared-data
18      mountPath: /data
19  volumes:
20  - name: shared-data
21    emptyDir: {}

Get pods.

1kubectl get pods -n demo
2NAME                     READY   STATUS      RESTARTS      AGE
3init-example             0/1     Init:0/1    0             4s

Check init container status.

 1kubectl describe pod init-example -n demo
 2Name:             init-example
 3Namespace:        demo
 4Priority:         0
 5Service Account:  default
 6Node:             master03/192.168.254.203
 7Start Time:       Tue, 10 Mar 2026 15:31:01 +0800
 8Labels:           <none>
 9Annotations:      cni.projectcalico.org/containerID: 36dbbac275093e3861699ea342852c340f231104e1fb0f0656d7900636c9a536
10                  cni.projectcalico.org/podIP: 172.16.235.37/32
11                  cni.projectcalico.org/podIPs: 172.16.235.37/32
12Status:           Running
13IP:               172.16.235.37
14IPs:
15  IP:  172.16.235.37
16Init Containers:
17  init-myservice:
18    Container ID:  containerd://46c42a97223d05df1ba36690ec82b23d03845c68ab1594d2c01064885d0ca7f5
19    Image:         busybox
20    Image ID:      docker.io/library/busybox@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f
21    Port:          <none>
22    Host Port:     <none>
23    Command:
24      sh
25      -c
26      echo "Initializing..." && sleep 5 && touch /data/ready
27    State:          Terminated
28      Reason:       Completed
29      Exit Code:    0
30      Started:      Tue, 10 Mar 2026 15:31:04 +0800
31      Finished:     Tue, 10 Mar 2026 15:31:09 +0800
32    Ready:          True
33    Restart Count:  0
34    Environment:    <none>
35    Mounts:
36      /data from shared-data (rw)
37      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-q8hzh (ro)
38Containers:
39  app:
40    Container ID:   containerd://4b702cf75c30f1222e8ecb3dec202bae3c3dd3a6978d5a33f56b58fd80358a09
41    Image:          nginx
42    Image ID:       docker.io/library/nginx@sha256:0236ee02dcbce00b9bd83e0f5fbc51069e7e1161bd59d99885b3ae1734f3392e
43    Port:           <none>
44    Host Port:      <none>
45    State:          Running
46      Started:      Tue, 10 Mar 2026 15:31:11 +0800
47    Ready:          True
48    Restart Count:  0
49    Environment:    <none>
50    Mounts:
51      /data from shared-data (rw)
52      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-q8hzh (ro)
53Conditions:
54  Type                        Status
55  PodReadyToStartContainers   True 
56  Initialized                 True 
57  Ready                       True 
58  ContainersReady             True 
59  PodScheduled                True 
60Volumes:
61  shared-data:
62    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
63    Medium:     
64    SizeLimit:  <unset>
65  kube-api-access-q8hzh:
66    Type:                    Projected (a volume that contains injected data from multiple sources)
67    TokenExpirationSeconds:  3607
68    ConfigMapName:           kube-root-ca.crt
69    Optional:                false
70    DownwardAPI:             true
71QoS Class:                   BestEffort
72Node-Selectors:              <none>
73Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
74                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
75Events:
76  Type    Reason     Age   From               Message
77  ----    ------     ----  ----               -------
78  Normal  Scheduled  48s   default-scheduler  Successfully assigned demo/init-example to master03
79  Normal  Pulling    47s   kubelet            Pulling image "busybox"
80  Normal  Pulled     46s   kubelet            Successfully pulled image "busybox" in 1.471s (1.471s including waiting). Image size: 2222260 bytes.
81  Normal  Created    46s   kubelet            Created container: init-myservice
82  Normal  Started    45s   kubelet            Started container init-myservice
83  Normal  Pulling    40s   kubelet            Pulling image "nginx"
84  Normal  Pulled     39s   kubelet            Successfully pulled image "nginx" in 1.565s (1.565s including waiting). Image size: 62944796 bytes.
85  Normal  Created    38s   kubelet            Created container: app
86  Normal  Started    38s   kubelet            Started container app