CertGrid CertGrid
Hands-on Lab·Certified Kubernetes Application Developer

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

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.

One Pod on one worker. Which worker is irrelevant here - everything demonstrated happens inside a single Pod sandbox.
Server NameIP AddressOSRolesCPURAMHDD
CKA1001192.168.0.175Ubuntu 26.04 LTSControl Plane Node2 Core4 GB50 GB
CKA1001-NODE01192.168.0.176Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB
CKA1001-NODE02192.168.0.177Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB
CKA1001-NODE03192.168.0.178Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB

Before you start

  1. One Pod, two containers

    Both containers are listed in the same spec.containers array and both mount the same emptyDir:

    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.209

    Expected resultBoth containers ready, one Pod IP.

    Success conditionYou have a two-container Pod to take apart.

  2. They share a network namespace

    The busybox container asks for http://localhost and 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, and netstat inside the *client* shows the *server's* listening socket:

    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN

    The 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                   :::*                    LISTEN

    Expected 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.

  3. They share any volume you mount in both

    The client writes, the server reads:

    written by the client

    An emptyDir is 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.volumes and 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 client

    Expected resultThe server reads the file the client wrote.

    Success conditionYou have the second of the two shared things.

  4. 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 directory

    And each container has its own PID 1:

    PID   COMMAND
        1 sh
    PID   COMMAND
        1 nginx

    Two separate process namespaces. A sidecar cannot kill -HUP the main process, cannot see it in ps, and cannot read its /proc. If you need that, shareProcessNamespace: true on the Pod merges them - and then PID 1 becomes a pause process and both applications appear in each other's ps.

    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 ps

    Expected resultA missing directory and two different PID 1s.

    Success conditionYou know the limits of a shared Pod, not just its capabilities.

  5. Logs need a container name

    With more than one container, kubectl logs has to pick:

    Defaulted container "server" out of: server, client

    It 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, -c is required for logs and exec whenever you want a specific one, and kubectl logs pod --all-containers=true gets 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" deleted

    Expected 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

Official sources