Home › CKAD practice tasks › Add an init container and a native sidecar
CKAD · Application Design and Build

Fix a Crash Loop with an Init Container and Add a Log Sidecar

Deployment `orders-api` in namespace `orders` is in CrashLoopBackOff. Its container `app` expects a configuration file that nothing creates, and the operations team also wants the application log shipped by a sidecar. The Pod already defines two `emptyDir` volumes: `config` and `logs`.

Live lab access

This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.

Create an account to continue →

Read this week's free task →

On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.

The task

Deployment orders-api in namespace orders is in CrashLoopBackOff. Its container app expects a configuration file that nothing creates, and the operations team also wants the application log shipped by a sidecar. The Pod already defines two emptyDir volumes: config and logs.

Requirements
1. Add an init container init-config (image busybox:1.36.1) that writes the single line mode=production to /work/config/app.conf in the config volume, then exits.
2. Add a native sidecar log-shipper (image busybox:1.36.1): it must be declared as an init container with restartPolicy: Always, mount the logs volume at /var/log/orders, and run tail -F /var/log/orders/app.log.
3. Ensure the rolled-out Pod runs app and log-shipper together, and that kubectl logs of log-shipper shows the application's order processed lines.

Keep the existing app container, volumes and mount paths unchanged.

Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. Native sidecar containers have been stable since Kubernetes v1.33, so they work on v1.35 without any feature gate.

Init containers and native sidecars: who starts first, and how they share files

A Pod's initContainers list is processed strictly in order. A normal init container must run to completion and exit successfully before the next entry starts. An entry with restartPolicy: Always is a native sidecar: the kubelet starts it, considers it done as soon as it has started, moves on, and keeps it running for the rest of the Pod's life. Only after the whole list is processed do the regular containers start. Since none of these containers share a filesystem by default, files travel through a volume, usually an emptyDir.

write-config (init)    runs, writes /cfg/app.env, exits 0
      v
status-reader (sidecar) started, stays up      reads  /out/status  <--+
      v                                                                 | emptyDir "out"
app (main)              sources /cfg/app.env, writes /out/status every 5s --+

1. Build the Pod from three roles

Save this as ticker.yaml. Two emptyDir volumes connect the containers: cfg carries one-way setup data from the init container to the app, and out carries the app's output to the sidecar.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ticker
  namespace: prepium-tutorial
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ticker
  template:
    metadata:
      labels:
        app: ticker
    spec:
      volumes:
      - name: cfg
        emptyDir: {}
      - name: out
        emptyDir: {}
      initContainers:
      - name: write-config            # 1st: runs once, must exit 0
        image: busybox:1.36
        command: ["sh", "-c", "echo colour=teal > /cfg/app.env"]
        volumeMounts:
        - name: cfg
          mountPath: /cfg
      - name: status-reader           # 2nd: native sidecar, keeps running
        image: busybox:1.36
        restartPolicy: Always
        command: ["sh", "-c", "while true; do cat /out/status 2>/dev/null; sleep 5; done"]
        volumeMounts:
        - name: out
          mountPath: /out
      containers:
      - name: app                     # starts after both of the above
        image: busybox:1.36
        command: ["sh", "-c", ". /cfg/app.env; while true; do echo \"$(date +%T) colour=$colour\" > /out/status; sleep 5; done"]
        volumeMounts:
        - name: cfg
          mountPath: /cfg
          readOnly: true
        - name: out
          mountPath: /out
kubectl create namespace prepium-tutorial
kubectl apply -f ticker.yaml
kubectl -n prepium-tutorial get pods -l app=ticker -w

While setup is in progress, STATUS reads Init:N/M: N of the M init-list entries have finished. When the main container is up, the Pod reports Running.

2. Check each container's state separately

POD=$(kubectl -n prepium-tutorial get pod -l app=ticker -o jsonpath='{.items[0].metadata.name}')
kubectl -n prepium-tutorial get pod $POD \
  -o jsonpath='{range .status.initContainerStatuses[*]}{.name}{"\t"}{.state}{"\n"}{end}'
kubectl -n prepium-tutorial logs $POD -c write-config
kubectl -n prepium-tutorial logs $POD -c status-reader --tail=3

write-config shows a terminated state with exit code 0. status-reader also lives under initContainerStatuses because of where it is declared, yet its state is running. Its log prints lines such as a timestamp followed by colour=teal, which proves both hand-offs: the app read the init container's file, and the sidecar read the app's file. Use -c every time; with several containers, naming the one you want avoids reading the wrong log.

3. Know the ordering guarantees

Common mistakes

Example checkEvidence
Init container finishedTerminated with exit code 0 in initContainerStatuses
Sidecar declared nativelyListed under initContainers with restartPolicy: Always, state running
Main container healthyRunning in containerStatuses, no restarts climbing
Data really sharedSidecar log shows content only the main container writes

Cleanup: kubectl delete namespace prepium-tutorial. References: Sidecar containers, Init containers, Debug init containers. Next, try sidecars in a Job or Pod debugging.

Difficulty
Medium · 8 minutes target
Domain
Application Design and Build
Grading
Live checks · partial credit

Practice scope

This additional curriculum lab is available in single-task practice with the existing 12-minute limit. The three CKAD mocks keep their original 17-task sets. Hints, an explained solution and requirement-by-requirement feedback are available in the lab.

Browse CKAD practice tasks →