Multi-container pods are the most reliably-tested CKAD topic — every exam includes at least one question, and they appear inside multiple other questions disguised as something else. The CCNCF officially names four patterns: init, sidecar, ambassador, and adapter. Each has a specific shape, a specific use case, and a specific YAML template you can adapt in seconds if you’ve practiced.
This guide gives you a hands-on walkthrough of all four, complete with copy-paste YAML, exam-style scenarios, and debugging recipes. By the end, you’ll recognize which pattern a question is asking for from its first sentence.
The Mental Model: Why Multi-Container Pods Exist
A Pod isn’t one container — it’s a unit of deployment that can hold one or more tightly-coupled containers sharing the same network namespace and (optionally) volumes. Containers in a pod can:
- Reach each other via
localhost - Share files through volumes (especially
emptyDir) - Start in a defined order (init containers first, then app containers)
Use multi-container pods when two processes are so coupled they must run on the same node and start/stop together — not because you have two services that happen to be related.
Pattern 1: Init Containers (the Most Frequently Tested)
Init containers run before app containers, in order, each to completion. If any fails, the pod restarts and runs init containers from the beginning.
When the Exam Asks for It
Watch for these phrases:
- “wait for the database to be ready”
- “fetch a config file before the app starts”
- “perform a setup task that the main container doesn’t need to know about”
- “initialize a volume before the app reads from it”
YAML Template
apiVersion: v1
kind: Pod
metadata:
name: web-with-init
spec:
initContainers:
- name: wait-for-db
image: busybox:1.28
command: ['sh', '-c', 'until nslookup db.default.svc.cluster.local; do sleep 2; done']
- name: fetch-config
image: busybox:1.28
command: ['sh', '-c', 'wget -O /config/app.json http://config-server/app.json']
volumeMounts:
- name: config
mountPath: /config
containers:
- name: web
image: nginx
volumeMounts:
- name: config
mountPath: /etc/app
volumes:
- name: config
emptyDir: {}
How to Build This Quickly on the Exam
# Generate a base pod
k run web-with-init --image=nginx $do > pod.yaml
# Add the initContainers block above 'containers:'
vim pod.yaml
The volumes section and volumeMounts make the init container’s output visible to the main container. This is the most common multi-container test on the exam.
Inspecting Init Container Status
# Watch init container progress
k get pod web-with-init
# Init container logs (use the init container's name)
k logs web-with-init -c wait-for-db
k logs web-with-init -c fetch-config
# Describe to see init container Events
k describe pod web-with-init
If a pod is stuck in Init:0/2 or PodInitializing, it means an init container is still running or failing. Use kubectl logs with the init container’s name to debug.
Pattern 2: Sidecar (Logs, Caching, Service Mesh)
A sidecar runs alongside the app container for the entire pod lifetime, providing supporting functionality. Unlike init containers, sidecars don’t finish — they’re long-running.
When the Exam Asks for It
Watch for:
- “ship logs from the app to a log aggregator”
- “transform output before sending it elsewhere”
- “cache responses from the main app”
- “sync a directory between the app and external storage”
YAML Template (Log Shipper Sidecar)
apiVersion: v1
kind: Pod
metadata:
name: app-with-logger
spec:
containers:
- name: app
image: busybox
command: ['sh', '-c', 'while true; do echo "$(date) hello" >> /var/log/app.log; sleep 5; done']
volumeMounts:
- name: logs
mountPath: /var/log
- name: log-shipper
image: busybox
command: ['sh', '-c', 'tail -F /var/log/app.log']
volumeMounts:
- name: logs
mountPath: /var/log
volumes:
- name: logs
emptyDir: {}
The two containers share the logs volume. The app writes; the sidecar reads.
Inspecting Each Container
# Logs from the main container
k logs app-with-logger -c app
# Logs from the sidecar
k logs app-with-logger -c log-shipper
# Exec into a specific container
k exec -it app-with-logger -c app -- sh
k exec -it app-with-logger -c log-shipper -- sh
The -c <container-name> flag is mandatory in multi-container pods. Forgetting it is the most common error.
Native Sidecar Containers (Kubernetes 1.29+)
Modern Kubernetes supports “native” sidecars via restartPolicy: Always on init containers — they run for the full pod lifetime but start before app containers:
spec:
initContainers:
- name: log-shipper
image: busybox
restartPolicy: Always # makes this a native sidecar
command: ['sh', '-c', 'tail -F /var/log/app.log']
volumeMounts:
- name: logs
mountPath: /var/log
containers:
- name: app
# ...
The 2026 CKAD curriculum may include native sidecar questions. Recognize the restartPolicy: Always on an initContainer as the signal.
Pattern 3: Ambassador (Proxy to External Services)
An ambassador container acts as a proxy between the main app and the outside world. The app talks to localhost:<port>; the ambassador handles routing, TLS, retries, or service discovery.
When the Exam Asks for It
Watch for:
- “the app expects to connect to a database on localhost, but the database lives elsewhere”
- “encrypt traffic between the pod and an external service”
- “route between staging and production based on a flag”
YAML Template
apiVersion: v1
kind: Pod
metadata:
name: app-with-ambassador
spec:
containers:
- name: app
image: busybox
command: ['sh', '-c', 'while true; do wget -qO- http://localhost:8080; sleep 10; done']
- name: ambassador
image: nginx
ports:
- containerPort: 8080
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-conf
configMap:
name: ambassador-config
The ambassador (nginx) listens on localhost:8080 inside the pod and proxies to wherever its ConfigMap directs it. The app simply talks to localhost:8080.
Why This Beats Direct Connections
The app code stays simple — no service discovery logic, no TLS handling, no retry logic. The ambassador owns all of that. Swap the ambassador image to change behavior without touching the app.
Pattern 4: Adapter (Normalize App Output)
An adapter container transforms the app’s output to match an expected format. Most often used for monitoring — the adapter scrapes the app’s native metrics format and re-exposes them in a standardized format (e.g., Prometheus exposition format).
When the Exam Asks for It
Watch for:
- “expose the app’s metrics in Prometheus format”
- “convert log lines to JSON for ingestion”
- “normalize the app’s output for an external consumer”
YAML Template
apiVersion: v1
kind: Pod
metadata:
name: app-with-adapter
spec:
containers:
- name: app
image: busybox
command: ['sh', '-c', 'while true; do echo "metric=$RANDOM" > /metrics/raw; sleep 5; done']
volumeMounts:
- name: metrics
mountPath: /metrics
- name: adapter
image: busybox
command: ['sh', '-c', 'while true; do awk -F= "{print \"my_metric \"\$2}" /metrics/raw > /metrics/prom; sleep 5; done']
volumeMounts:
- name: metrics
mountPath: /metrics
volumes:
- name: metrics
emptyDir: {}
The app writes raw metrics to a shared volume; the adapter transforms them into Prometheus format. The two containers don’t need to know about each other — they just share a directory.
Choosing the Right Pattern: a Decision Tree
| Question hint | Pattern |
|---|---|
| ”Run X before the app starts” | Init container |
| ”Wait for Y to be ready” | Init container |
| ”Initialize a volume” | Init container |
| ”Ship logs / metrics from the app” | Sidecar |
| ”Long-running helper process” | Sidecar |
| ”App talks to localhost, real service is elsewhere” | Ambassador |
| ”Encrypt / proxy outgoing requests” | Ambassador |
| ”Transform output to a different format” | Adapter |
| ”Expose metrics in Prometheus format” | Adapter |
When in doubt, init container is the safe default for “do something before the app starts,” and sidecar is the safe default for “long-running helper.”
Shared Resources: Volumes and Networking
All containers in a pod share:
- Network namespace — they reach each other on
localhost. They cannot bind the same port. - Volumes — but only ones explicitly mounted in each container’s
volumeMounts. - IPC namespace (optional) — for shared memory.
They do NOT share:
- Filesystems by default. Use a shared
emptyDirvolume. - Environment variables — each container has its own.
- Process namespace by default. Set
shareProcessNamespace: trueto share.
The most common mistake on the exam: writing a sidecar that needs to read the app’s logs but forgetting to mount the same volume in both containers. Always mount the shared volume in every container that needs access.
Debugging Multi-Container Pods
# Pod stuck in Init or PodInitializing
k describe pod <pod>
k logs <pod> -c <init-container-name>
# One container failing (other is fine)
k get pod <pod> # READY shows 1/2
k describe pod <pod> # Find which container is failing
k logs <pod> -c <container-name> --previous
# Containers can't see shared volume
k describe pod <pod> | grep -A 5 Volumes
k describe pod <pod> | grep -A 5 Mounts
# Two containers can't talk on localhost
# Verify both are running, check their containerPorts, no port conflict
For broader application-level diagnosis, see our CKAD application troubleshooting guide.
Common Multi-Container Question Variants
Variant 1: Add an Init Container to an Existing Pod
“Modify pod
webto wait for servicedbto be reachable before starting the main container.”
Solution: edit pod, add an init container that loops on nslookup until DNS resolves.
Variant 2: Add a Sidecar That Reads App Logs
“Add a sidecar to pod
appthat tails/var/log/app.logand prints to stdout.”
Solution: add a busybox container with tail -F and mount the same volume.
Variant 3: Three-Container Pod with Init + Sidecar
“Create pod
analyticswith: an init container that fetches a config file, an app container that processes data, and a sidecar that ships processed output.”
Solution: combine patterns. One init container, two main containers, one shared volume.
Variant 4: Diagnose Why a Multi-Container Pod Is Failing
“Pod
etlshows 1/2 Ready. Find and fix the issue.”
Solution: kubectl describe pod etl, identify which container is failing, then kubectl logs etl -c <container>. Usual causes: wrong image, wrong command, missing volume mount.
How to Practice This Topic
Build these on a kind cluster, in order:
- A pod with one init container and one main container, sharing an
emptyDir. - A pod with a sidecar tailing the main container’s logs.
- A pod with an ambassador proxying HTTP traffic.
- A pod with an adapter transforming text output.
- A pod combining init + sidecar + main container.
Time yourself on each. Aim for under 4 minutes per template once you’ve memorized the patterns.
Validate Your Speed Under Exam Conditions
Drilling on your own cluster builds the muscle memory. The CKAD tests whether you can do this under time pressure with unfamiliar prompts. Take a full-length scored simulator with our CKAD Mock Exam Bundle — every simulator includes multi-container questions with the same UI and scoring rubric as the real exam.
Frequently Asked Questions
Q: How many multi-container pod questions appear on the CKAD? A: Typically 2-4 questions, including variants buried inside other tasks (e.g., “deploy this app with a sidecar”). Total weight: ~15-20%.
Q: Do I need to memorize all four patterns? A: Memorize init and sidecar — those are 80%+ of multi-container questions. Recognize ambassador and adapter so you can apply them when the question’s wording calls for them.
Q: Can a pod have multiple init containers? A: Yes. They run sequentially in the order listed. If any fails, all init containers run again from the start when the pod restarts.
Q: Do init containers share volumes with main containers?
A: Only if both mount the same volume in their volumeMounts. The volume itself is defined once in the pod’s spec.volumes.
Q: What happens if a sidecar crashes?
A: With normal containers, the pod’s RestartPolicy applies (default: Always). With native sidecars (Kubernetes 1.29+ initContainers with restartPolicy: Always), they restart independently without restarting the entire pod.
Q: How do I run a command in a specific container?
A: kubectl exec -it <pod> -c <container-name> -- <command>. The -c flag is required when the pod has multiple containers.
Q: Can two containers in the same pod listen on the same port? A: No. They share the network namespace, so port conflicts apply just like on a single host.
Ready to make multi-container patterns automatic points on your CKAD? Run a scored full-length simulator with our CKAD Mock Exam Bundle and find out exactly where you’d score today.