Add Init Containers

KubernetesBeginner
Practice Now

Introduction

An init container runs before the main application containers in a Pod. Each init container must finish successfully before Kubernetes starts the next init container or any regular container. This makes init containers useful for preparing files, checking dependencies, or performing small setup tasks that should not stay running beside the application.

In this lab, you will add an init container that writes a startup file into a shared emptyDir volume, confirm that the application container can read the file, inspect the init container's completed status and logs, and then use the same pattern in a Deployment template.

Prepare an Init Container Namespace

In this step, you will prepare a clean namespace for init container practice.

Start in /home/labex/project. You will write Pod and Deployment manifests in this directory.

mkdir -p /home/labex/project
cd /home/labex/project

Check that the Kubernetes API server is reachable. If the VM has just started and the node is not ready yet, wait a few seconds and run the command again.

kubectl get nodes --request-timeout=5s

Expected output includes:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

Init containers run inside Pods, so wait until the cluster networking Pod is Ready before creating workloads.

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

Expected output:

pod/calico-node-... condition met

Create a namespace named ckad-prep-init:

kubectl create namespace ckad-prep-init

Set this namespace as the current context namespace:

kubectl config set-context --current --namespace=ckad-prep-init

Confirm the current namespace:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

Expected output:

ckad-prep-init

Prepare Shared Data Before the App Starts

In this step, you will create a Pod where an init container prepares a file before the application container starts.

The emptyDir volume is created when the Pod is assigned to a node and is shared by containers in the same Pod. Both the init container and the app container mount this volume at /work. The init container writes /work/startup.txt, then exits. Only after it exits successfully does Kubernetes start the app container.

Create init-file-pod.yaml:

cat > init-file-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: init-file-demo
  labels:
    app: init-file-demo
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: shared-work
    emptyDir: {}
  initContainers:
  - name: prepare-startup-file
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo init-started; echo prepared-by-init > /work/startup.txt; echo init-finished'
    volumeMounts:
    - name: shared-work
      mountPath: /work
  containers:
  - name: app
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo app-started; cat /work/startup.txt; sleep 3600'
    volumeMounts:
    - name: shared-work
      mountPath: /work
EOF

Apply the manifest:

kubectl apply -f init-file-pod.yaml

Expected output:

pod/init-file-demo created

Wait until the Pod is Ready:

kubectl wait --for=condition=Ready pod/init-file-demo --timeout=120s

Expected output:

pod/init-file-demo condition met

Read the file from inside the app container. This proves that the init container prepared data on the shared volume before the app started.

kubectl exec init-file-demo -c app -- cat /work/startup.txt

Expected output:

prepared-by-init

Inspect Init Container Status and Logs

In this step, you will inspect evidence that the init container completed before the app container kept running.

Init containers appear under .status.initContainerStatuses, while regular containers appear under .status.containerStatuses. The init container should have a Completed reason because it exited successfully.

kubectl get pod init-file-demo -o 'jsonpath={.status.initContainerStatuses[0].name}{" "}{.status.initContainerStatuses[0].state.terminated.reason}{"\n"}'

Expected output:

prepare-startup-file Completed

Now inspect the regular application container status. It should be ready and running.

kubectl get pod init-file-demo -o 'jsonpath={.status.containerStatuses[0].name}{" "}{.status.containerStatuses[0].ready}{" "}{.status.containerStatuses[0].state.running.startedAt}{"\n"}'

Expected output includes:

app true ...

Read the init container logs. Even though the init container has finished, its logs remain available through kubectl logs -c.

kubectl logs init-file-demo -c prepare-startup-file

Expected output:

init-started
init-finished

Read the app container logs. The app starts after the init container writes the file, so its logs include the prepared value.

kubectl logs init-file-demo -c app

Expected output includes:

app-started
prepared-by-init

Add an Init Container to a Deployment

In this step, you will use the same init container pattern inside a Deployment template.

For application workloads, you usually place init containers in a controller's Pod template instead of creating standalone Pods. When the Deployment creates a Pod, Kubernetes runs the init container first for each Pod created from that template.

Create init-deployment.yaml:

cat > init-deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: init-web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: init-web
  template:
    metadata:
      labels:
        app: init-web
    spec:
      terminationGracePeriodSeconds: 0
      volumes:
      - name: web-content
        emptyDir: {}
      initContainers:
      - name: render-index
        image: busybox:1.36
        command:
        - /bin/sh
        - -c
        - 'echo deployment-init > /site/index.txt'
        volumeMounts:
        - name: web-content
          mountPath: /site
      containers:
      - name: web
        image: busybox:1.36
        command:
        - /bin/sh
        - -c
        - 'cat /site/index.txt; sleep 3600'
        volumeMounts:
        - name: web-content
          mountPath: /site
EOF

Apply the Deployment:

kubectl apply -f init-deployment.yaml

Expected output:

deployment.apps/init-web created

Wait until the Deployment is available:

kubectl wait --for=condition=Available deployment/init-web --timeout=120s

Expected output:

deployment.apps/init-web condition met

Find the Pod created by the Deployment:

web_pod="$(kubectl get pods -l app=init-web -o 'jsonpath={.items[0].metadata.name}')"
echo "$web_pod"

Expected output shows a generated Pod name:

init-web-...

Read the app container logs from that Pod:

kubectl logs "$web_pod" -c web

Expected output:

deployment-init

Inspect the init container completion status inside the Deployment-created Pod:

kubectl get pod "$web_pod" -o 'jsonpath={.status.initContainerStatuses[0].name}{" "}{.status.initContainerStatuses[0].state.terminated.reason}{"\n"}'

Expected output:

render-index Completed

Summary

You added init containers to a Pod and a Deployment template. You used an emptyDir volume to share prepared files with the app container, inspected completed init container status, read init and app logs, and confirmed that the same pattern works for controller-managed application Pods.