Multi-Container Pods: Shared Network and Volumes
Every multi-container pattern on the CKAD - sidecar, adapter, ambassador - is built out of exactly two shared things: one network namespace and any volumes you mount in both containers. Everything else stays separate, including the filesystem and the process list. This proves all four points on one Pod, because knowing exactly what is shared is what tells you whether a design needs one Pod or two.
Application Design and Build Guide 7 of 44 Beginner
- Kubernetes1.36.4
- Runtimecontainerd 2.2.6
- CNICalico v3.32.1
- TimeAbout 14 min
- Reviewed23 August 2026
Written against the versions above. Process namespaces are separate unless you set `shareProcessNamespace: true` on the Pod, which is not set here - each container's `ps` shows its own PID 1. That default is why a sidecar cannot signal the main process without it.
| 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 and kubectl.
- The session creates namespace
ckad-multiand one Pod namedpair: annginx:alpineserver and abusyboxclient, sharing oneemptyDir.
-
One Pod, two containers
Both containers are listed in the same
spec.containersarray and both mount the sameemptyDir:POD READY IP pair true,true ...One Pod, one IP, two containers. That single IP is the whole reason the next step works.
bash Example session kubectl create namespace ckad-multinamespace/ckad-multi createdkubectl -n ckad-multi wait --for=condition=Ready pod/pair --timeout=120spod/pair condition metkubectl -n ckad-multi get pod pair -o 'custom-columns=POD:.metadata.name,READY:.status.containerStatuses[*].ready,IP:.status.podIP'POD READY IPpair true,true 10.244.86.209Expected resultBoth containers ready, one Pod IP.
Success conditionYou have a two-container Pod to take apart.
-
They share a network namespace
The busybox container asks for
http://localhostand nginx answers:<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title>There is no Service, no DNS and no Pod IP in that command. The two containers are in the same network namespace, so one can reach the other on
localhost, andnetstatinside the *client* shows the *server's* listening socket:tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTENThe consequence people trip over: two containers in one Pod cannot both use port 80. They are competing for the same namespace, and the second one to bind fails.
bash Example session kubectl -n ckad-multi exec pair -c client -- wget -qO- --timeout=4 http://localhost | head -4<!DOCTYPE html><html><head><title>Welcome to nginx!</title>kubectl -n ckad-multi exec pair -c client -- netstat -tlnActive Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address Statetcp 0 0 0.0.0.0:80 0.0.0.0:* LISTENtcp 0 0 :::80 :::* LISTENExpected resultnginx's page fetched over localhost, and its socket visible from the other container.
Success conditionYou can explain why sidecars talk over localhost and why ports collide.
-
They share any volume you mount in both
The client writes, the server reads:
written by the clientAn
emptyDiris created empty when the Pod is scheduled and deleted when the Pod is deleted, and any container that mounts it sees the same files. This is the second half of every multi-container pattern - a log-shipping sidecar reads the application's log directory this way, and an adapter writes a reformatted file back.Note it is opt-in per container. A volume declared in
spec.volumesand mounted in only one container is shared with nobody.bash Example session kubectl -n ckad-multi exec pair -c client -- sh -c 'echo "written by the client" > /shared/note.txt'kubectl -n ckad-multi exec pair -c server -- cat /shared/note.txtwritten by the clientExpected resultThe server reads the file the client wrote.
Success conditionYou have the second of the two shared things.
-
What they do not share
The filesystem outside that volume is entirely separate. nginx's config directory does not exist for the busybox container:
ls: /etc/nginx: No such file or directoryAnd each container has its own PID 1:
PID COMMAND 1 shPID COMMAND 1 nginxTwo separate process namespaces. A sidecar cannot
kill -HUPthe main process, cannot see it inps, and cannot read its/proc. If you need that,shareProcessNamespace: trueon the Pod merges them - and then PID 1 becomes a pause process and both applications appear in each other'sps.So the rule for deciding one Pod or two: share a network namespace and files, and are always scheduled and scaled together -> one Pod. Anything else -> two.
bash Example session kubectl -n ckad-multi exec pair -c client -- ls /etc/nginxls: /etc/nginx: No such file or directorycommand terminated with exit code 1[exit 1]kubectl -n ckad-multi exec pair -c client -- ps -o pid,commPID COMMAND 1 sh 32 pskubectl -n ckad-multi exec pair -c server -- ps -o pid,commPID COMMAND 1 nginx 31 nginx 32 nginx 39 psExpected resultA missing directory and two different PID 1s.
Success conditionYou know the limits of a shared Pod, not just its capabilities.
-
Logs need a container name
With more than one container,
kubectl logshas to pick:Defaulted container "server" out of: server, clientIt tells you which one it chose - and in the output you can see the client's request arriving:
127.0.0.1 - - [23/Aug/2026:03:05:02 +0000] "GET / HTTP/1.1" 200 896 "-" "Wget" "-"That log line is the proof of step 2 from the other side: nginx recorded a request from
127.0.0.1, from a process in a different container.On a multi-container Pod,
-cis required forlogsandexecwhenever you want a specific one, andkubectl logs pod --all-containers=truegets everything at once.bash Example session kubectl -n ckad-multi logs pairDefaulted container "server" out of: server, client/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d//docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.shkubectl -n ckad-multi logs pair -c server --tail=32026/08/23 03:05:01 [notice] 1#1: start worker process 312026/08/23 03:05:01 [notice] 1#1: start worker process 32127.0.0.1 - - [23/Aug/2026:03:05:02 +0000] "GET / HTTP/1.1" 200 896 "-" "Wget" "-"kubectl delete namespace ckad-multi --wait=falsenamespace "ckad-multi" deletedExpected resultThe defaulting message, and nginx's access log showing 127.0.0.1.
Success conditionYou can read and exec into the container you meant.
Troubleshooting
The second container will not start - address already in use.
Why: Both containers bind the same port in one shared network namespace.
Fix:Change one container's port. Containers in a Pod cannot both hold a port.
A sidecar cannot see the application's files.
Why: The volume is mounted in only one container.
Fix:Add the same
volumeMountsentry to both. Declaring the volume is not enough.kubectl logsreturns the wrong container's output.Why: It defaulted to the first container.
Fix:
-c <name>, or--all-containers=true. The defaulting message names what it picked.A sidecar cannot signal the main process.
Why: Separate PID namespaces by default.
Fix:Set
shareProcessNamespace: trueon the Pod spec.