Sidecar Containers in Jobs
A Job is complete when its Pod's containers have all terminated. A sidecar by definition never terminates, so a Job with an ordinary sidecar runs forever with the actual work finished in the first second. The native sidecar - an init container with `restartPolicy: Always` - exists precisely to fix this, and both Jobs are run here side by side so the difference is one table.
Application Design and Build Guide 9 of 44 Intermediate
- Kubernetes1.36.4
- Runtimecontainerd 2.2.6
- CNICalico v3.32.1
- TimeAbout 15 min
- Reviewed23 August 2026
Written against the versions above. Native sidecars went stable in Kubernetes 1.33 and are used here on 1.36. A container in `initContainers` with `restartPolicy: Always` starts before the application containers, keeps running alongside them, and is excluded from the Job's completion calculation. On older clusters the workaround was to have the sidecar watch for a marker file and exit itself.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA1001 | 192.168.0.175 | Ubuntu 26.04 LTS | Control Plane Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE01 | 192.168.0.176 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE02 | 192.168.0.177 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE03 | 192.168.0.178 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
Before you start
- A cluster on 1.33 or newer for the native sidecar half.
- The session creates namespace
ckad-sideand two Jobs,brokenandfixed, differing only in where the helper container is declared.
-
A Job with an ordinary sidecar
Two containers:
workwrites a file and exits,helperloops forever. Thirty seconds later:NAME STATUS COMPLETIONS DURATION AGE broken Running 0/1 30s 30s0/1, stillRunning. But the work is done - it logged its line in the first second:finishedThe Pod confirms it: one container ready, one not.
POD PHASE READY broken-hfdvj Running true,falseThe Job will never complete. Its Pod cannot reach a terminal phase while a container is still running, and the helper has no reason to stop. With no
activeDeadlineSecondsthis runs until somebody notices.bash Example session kubectl create namespace ckad-sidenamespace/ckad-side createdsleep 30; kubectl -n ckad-side get job brokenNAME STATUS COMPLETIONS DURATION AGEbroken Running 0/1 30s 30skubectl -n ckad-side get pods -l batch.kubernetes.io/job-name=broken -o 'custom-columns=POD:.metadata.name,PHASE:.status.phase,READY:.status.containerStatuses[*].ready'POD PHASE READYbroken-hfdvj Running true,falsekubectl -n ckad-side logs -l batch.kubernetes.io/job-name=broken -c workfinishedExpected result
Running 0/1with the work container's output already present.Success conditionYou have reproduced a Job that is finished and does not know it.
-
The same Job with a native sidecar
The only change:
helpermoves fromcontainerstoinitContainers, and gainsrestartPolicy: Always.NAME STATUS COMPLETIONS DURATION AGE fixed Complete 1/1 34s 34sPOD PHASE fixed-kk2fz SucceededComplete. The helper still ran - it started *before* the work container, which is the other half of what native sidecars give you, and is why they suit a proxy or a log shipper that has to be up before the app produces anything. But it is not counted, so when
workexited the Pod succeeded and Kubernetes shut the sidecar down.That ordering guarantee is the reason this is not just a flag on a normal container. An ordinary sidecar starts *at the same time* as the app and may miss its first output.
bash Example session kubectl -n ckad-side wait --for=condition=Complete job/fixed --timeout=120sjob.batch/fixed condition metkubectl -n ckad-side get job fixedNAME STATUS COMPLETIONS DURATION AGEfixed Complete 1/1 34s 34skubectl -n ckad-side get pods -l batch.kubernetes.io/job-name=fixed -o 'custom-columns=POD:.metadata.name,PHASE:.status.phase'POD PHASEfixed-kk2fz SucceededExpected result
Complete 1/1and aSucceededPod.Success conditionYou fixed a hanging Job by moving one container in the manifest.
-
Side by side
JOB COMPLETIONS ACTIVE broken <none> 1 fixed 1 <none>One line each.
brokenhas no completions and an active Pod;fixedhas its completion and nothing running.The rule worth keeping: any container that does not exit on its own belongs in
initContainerswithrestartPolicy: Always, whenever the workload is a Job or a CronJob. In a Deployment it makes no difference to completion - nothing is ever expected to finish - but the start-ordering guarantee is still usually what you want.If you are stuck on an older cluster, the escape hatch is
activeDeadlineSecondson the Job, which kills the Pod after a set time. It marks the JobFailedrather than complete, so it is damage control and not a fix.bash Example session kubectl -n ckad-side get jobs -o 'custom-columns=JOB:.metadata.name,COMPLETIONS:.status.succeeded,ACTIVE:.status.active'JOB COMPLETIONS ACTIVEbroken <none> 1fixed 1 <none>kubectl -n ckad-side delete job brokenjob.batch "broken" deleted from ckad-side namespacekubectl delete namespace ckad-side --wait=falsenamespace "ckad-side" deletedExpected resultThe two Jobs in one table, one active and one complete.
Success conditionYou can state the rule and show the evidence for it.
Troubleshooting
A Job stays
Runninglong after the work is done.Why: A container in the Pod has not exited - usually a sidecar.
Fix:Move it to
initContainerswithrestartPolicy: Always.The sidecar misses the application's first output.
Why: Ordinary containers start concurrently, in no guaranteed order.
Fix:A native sidecar is started and ready before the application containers begin.
restartPolicy: Alwayson an init container is rejected.Why: The cluster predates the stable native sidecar support.
Fix:Check
kubectl version. Before 1.29 the field is not accepted at all; use the marker-file workaround oractiveDeadlineSeconds.A CronJob's runs pile up because none of them finish.
Why: The same sidecar problem, once per schedule tick.
Fix:Fix the sidecar, and set
concurrencyPolicy: Forbid- see guide 24.