Kubernetes Sidecar and Multi-Container Patterns with Examples

Tested on Rocky Linux 10.2 (Red Quartz) workstation
Package kubectl 1.36.3
Applies to Any host with kubectl configured; Kubernetes v1.33 or later
Cert prep CKAD
Lab environment Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm
Privilege Normal user (no sudo required on the workstation)
Scope Multi-container Pods, localhost and shared-volume communication, native sidecar lab, adapter and ambassador patterns, startup behaviour, resource overview, and common problems. Does not cover init-container sequencing depth, Jobs, service mesh, Ingress, PV provisioning, or resource limits depth.
Related guides Kubernetes health probes

A sidecar container runs beside your main application in the same Pod, sharing its network namespace and optional volumes. This walkthrough builds one native sidecar Pod in the sidecar-lab namespace: the content-writer sidecar keeps index.html fresh on a shared emptyDir volume while nginx serves it over HTTP. Native sidecars are stable from Kubernetes v1.33. They were available as a beta feature and enabled by default in Kubernetes v1.29 through v1.32.


What Is a Multi-Container Pod?

A Pod can run more than one tightly coupled container.

  • Containers belong to the same Pod, run on the same node, and are created or removed as one application unit
  • They can share files by mounting the same volume
  • Each container still has its own image, filesystem, and process environment
  • The one-container-per-Pod model remains appropriate when components do not need close coordination

For Pod YAML, phases, and lifecycle detail, see Kubernetes Pods and Pod Lifecycle.


How Containers Communicate Inside a Pod

Shared network

All containers in a Pod use the same Pod IP address and port namespace.

  • Processes reach each other on localhost
  • Each listener must use a different port inside the Pod
  • No separate Service is required for container-to-container traffic within the Pod

If nginx listens on port 80 in the web container, another container can fetch it with http://localhost:80.

Shared volumes

Container filesystems are separate. To exchange files, both containers mount the same named volume at paths that suit each image:

  • Sidecar writes to /shared/index.html
  • Application reads from /usr/share/nginx/html/index.html
  • The volume name (shared) ties the mounts together
yaml
spec:
  initContainers:
    - name: content-writer
      volumeMounts:
        - name: shared
          mountPath: /shared

  containers:
    - name: web
      volumeMounts:
        - name: shared
          mountPath: /usr/share/nginx/html

  volumes:
    - name: shared
      emptyDir: {}

For volume types and persistence beyond scratch space, see Kubernetes volumes.


Kubernetes Sidecar Pattern

A sidecar extends or supports the main application without replacing its primary function.

Use case Example helper
Log processing Fluent Bit or Filebeat beside the app container
Configuration sync Container that refreshes config files on a shared volume
Local proxying Ambassador that forwards localhost traffic to a remote API
Metrics collection Prometheus exporter scraping localhost metrics
File generation content-writer in this lab, updating index.html on a shared volume

Create a native sidecar using a shared volume

Kubernetes v1.33 made native sidecars stable. The primary lab uses a native sidecar declared under spec.initContainers with container-level restartPolicy: Always. Save this manifest as sidecar-web.yaml:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  namespace: sidecar-lab
spec:
  initContainers:
    - name: content-writer
      image: busybox:1.36
      restartPolicy: Always
      command:
        - sh
        - -c
      args:
        - |
          while true; do
            message="Sidecar updated at $(date -u +%Y-%m-%dT%H:%M:%SZ)"
            echo "$message" | tee /shared/index.html
            sleep 30
          done
      startupProbe:
        exec:
          command:
            - sh
            - -c
            - test -s /shared/index.html
        periodSeconds: 1
        failureThreshold: 30
      volumeMounts:
        - name: shared
          mountPath: /shared

  containers:
    - name: web
      image: nginx:1.27-alpine
      volumeMounts:
        - name: shared
          mountPath: /usr/share/nginx/html

  volumes:
    - name: shared
      emptyDir: {}

The sidecar writes index.html to /shared; nginx serves the same file from /usr/share/nginx/html because both mount the shared volume. The startupProbe ensures the sidecar has created index.html before Kubernetes starts the web container. Native sidecars start in init-container order, and Kubernetes proceeds after the sidecar has started or its startup probe succeeds.

Create the namespace:

bash
kubectl create namespace sidecar-lab

Sample output:

output
namespace/sidecar-lab created

Apply the Pod:

bash
kubectl apply -f sidecar-web.yaml

Sample output:

output
pod/web created

Wait until both containers are Ready before you inspect the Pod:

bash
kubectl wait --for=condition=Ready pod/web -n sidecar-lab --timeout=60s

Sample output:

output
pod/web condition met

Confirm both containers are running:

bash
kubectl get pod web -n sidecar-lab

Sample output:

output
NAME   READY   STATUS    RESTARTS   AGE
web    2/2     Running   0          3s

The 2/2 count includes the native sidecar and the web container. In this example, the startupProbe delays nginx startup until the sidecar creates index.html; it is not an ongoing readiness check. A readinessProbe can be added when sidecar health should continue to affect Pod readiness.

Read the content nginx serves from the shared file:

bash
kubectl exec web -n sidecar-lab -c web -- wget -qO- http://localhost/

Sample output:

output
Sidecar updated at 2026-07-26T06:49:57Z

Confirm both containers see the same volume through different mount paths. Write a fixed test file from the sidecar:

bash
kubectl exec web -n sidecar-lab -c content-writer -- sh -c 'echo shared-volume-test > /shared/check.txt'

Read it through the nginx container's mount path:

bash
kubectl exec web -n sidecar-lab -c web -- cat /usr/share/nginx/html/check.txt

Sample output:

output
shared-volume-test

The file written at /shared/check.txt in the sidecar appears at /usr/share/nginx/html/check.txt in nginx because both paths mount the same shared volume.

View logs from individual containers

Kubernetes needs a container name when a Pod runs more than one container:

bash
kubectl logs web -n sidecar-lab -c web

Sample output:

output
/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.sh

The sidecar writes each update to stdout through tee:

bash
kubectl logs web -n sidecar-lab -c content-writer --tail=1

Sample output:

output
Sidecar updated at 2026-07-26T06:49:57Z

The -c flag selects one container. Use --all-containers=true when you want logs from every container in the Pod, including restartable init containers such as native sidecars:

bash
kubectl logs web -n sidecar-lab --all-containers=true --prefix

Sample output:

output
[pod/web/content-writer] Sidecar updated at 2026-07-26T06:57:42Z
[pod/web/web] 2026/07/26 06:57:43 [notice] 1#1: start worker processes
[pod/web/web] 2026/07/26 06:57:43 [notice] 1#1: start worker process 30

--prefix adds the Pod and container name to each line so you can distinguish nginx output from sidecar output.

For container state, mounts, and Events:

bash
kubectl describe pod web -n sidecar-lab

Native Sidecars vs Regular Helper Containers

This lab uses the current native sidecar form under spec.initContainers with restartPolicy: Always. Regular helper containers remain valid when startup ordering and finite-workload completion behaviour do not matter.

In the traditional pattern, the application and helper are both declared under spec.containers:

yaml
spec:
  containers:
    - name: web
      image: nginx:1.27-alpine
    - name: log-agent
      image: busybox:1.36
      command: ['sh', '-c', 'sleep infinity']

Kubernetes starts them without a guaranteed order, and both follow the Pod-level restart policy. Kubernetes still supports ordinary multi-container Pods, but they are not marked as native sidecars.

Concern Regular helper (spec.containers) Native sidecar (initContainers + restartPolicy: Always)
Startup order No guaranteed order Starts before application containers
Restart policy Pod-level Container-level Always
Probes during Pod lifetime Same as any app container Supported on the sidecar
Job completion Long-running helper can block Pod completion Stopped after application containers finish

For sequential init containers that must complete before the application runs, see Kubernetes init containers — that workflow is separate from native sidecars.


Kubernetes Adapter Pattern

An adapter container transforms application output into a format another system expects.

Example flow:

text
Application → shared volume → adapter → standard output

Typical shape:

  • Application container writes logs or metrics in its own format to a shared volume
  • Adapter container reads those files from a different mount path
  • Adapter emits JSON lines, Prometheus text, or another standard format
yaml
spec:
  containers:
    - name: application
      image: app-image:1.0
      volumeMounts:
        - name: logs
          mountPath: /var/log/app

    - name: log-adapter
      image: adapter-image:1.0
      args:
        - --input=/input/app.log
        - --output-format=json
      volumeMounts:
        - name: logs
          mountPath: /input

  volumes:
    - name: logs
      emptyDir: {}

This abbreviated pattern assumes an adapter image that reads the application log and performs proper JSON encoding; adapter-image:1.0 is a placeholder rather than a runnable public image.

You can adapt the sidecar lab by replacing content-writer with a container that reads /shared/app.log and emits JSON lines — the volume mount pattern stays the same.


Kubernetes Ambassador Pattern

An ambassador acts as a local proxy between the application and a remote service.

Example flow:

text
Application → localhost → ambassador → external service

Typical shape:

  • Application connects to localhost:8080 and treats the ambassador as the real service
  • Ambassador container listens on that port inside the shared Pod network namespace
  • Ambassador forwards traffic to an external database, cache, or API

The following is an abbreviated Pod-template fragment. Replace proxy-image:1.0 and its environment variables with the configuration supported by the proxy you choose.

yaml
spec:
  containers:
    - name: application
      image: app-image:1.0
      env:
        - name: DB_HOST
          value: localhost
        - name: DB_PORT
          value: "8080"

    - name: ambassador
      image: proxy-image:1.0
      ports:
        - containerPort: 8080
      env:
        - name: UPSTREAM
          value: postgres.example.svc.cluster.local:5432

The application stays unaware of the remote address. This article does not cover service mesh, Ingress, or full proxy configuration — only the Pod-local pattern.


Sidecar vs Adapter vs Ambassador

Pattern Main purpose Typical communication Example
Sidecar Extend or support the application Shared volume or localhost nginx + content-writer in this lab
Adapter Transform application output Shared volume or local stream App writes plain logs; adapter emits JSON
Ambassador Proxy external communication Localhost network connection App uses localhost:8080; ambassador reaches a remote API

Adapter and ambassador are specialised multi-container shapes. In practice they are often described as sidecar patterns because the helper runs beside the main container in the same Pod.


Container Startup and Failure Behaviour

Regular containers under spec.containers do not have guaranteed startup ordering. Do not assume a sidecar is ready the moment the main container starts — use retries, probes, or a native sidecar when ordering matters.

  • Regular application containers follow the Pod-level restartPolicy
  • Native sidecars use container-level restartPolicy: Always and restart independently
  • All containers remain scheduled on the same node
  • Native sidecars are stopped after the regular application containers terminate
Scenario What happens
App container crashes Kubelet restarts it inside the same Pod name when restartPolicy is Always
Native sidecar crashes Sidecar restarts independently; app container keeps running
Helper under spec.containers in a Job Long-running loop can keep the Pod from completing
Native sidecar in a Job Sidecar stops after the main container finishes; Job can complete

For guaranteed preparation before application containers start, use Kubernetes init containers.


Resource Requests in Multi-Container Pods

Each container can define its own requests and limits:

  • The scheduler considers the combined requirements of the entire Pod
  • A supporting container still consumes CPU and memory on the node
  • An unhealthy or resource-starved sidecar can affect the whole Pod
yaml
initContainers:
  - name: content-writer
    image: busybox:1.36
    restartPolicy: Always
    resources:
      requests:
        memory: 32Mi
        cpu: 50m

containers:
  - name: web
    image: nginx:1.27-alpine
    resources:
      requests:
        memory: 64Mi
        cpu: 100m

In this example, the scheduler reserves 150m CPU and 96Mi of memory for the concurrently running application and native sidecar. When regular init containers are also present, Kubernetes calculates the effective Pod request by comparing this combined request with the effective init-container requirement.

Native-sidecar requests participate in the Pod's effective scheduling request differently from regular sequential init containers.

For requests, limits, and QoS class detail, see Kubernetes resource requests and limits.


When Should Containers Share a Pod?

Situation Recommended placement Example
Log shipper must read files the app writes Same Pod nginx and Fluent Bit sharing a volume
Components scale at different rates Separate Pods Frontend and API Deployments
Helper must proxy localhost traffic Same Pod Application and ambassador
Stable Service endpoint for one tier Separate Pods API Deployment exposed through a Service
One-time migration before application startup Same Pod, regular init container Init container completes before the app starts

Regular init containers execute in the same Pod before application containers.


Common Multi-Container Pod Problems

Symptom Likely cause What to check
kubectl logs asks for a container Pod has multiple containers Add -c <name> or --all-containers=true
Shared file is missing Containers do not mount the same volume Compare volumes and both volumeMounts
Localhost connection is refused Wrong port, startup race, or process stopped Check listeners, container state, and probes
Sidecar repeatedly restarts Its command exits or fails Check sidecar logs, exit code, and restart count
Finite workload does not complete Long-running helper is under spec.containers Use a native sidecar or redesign the helper lifecycle

What's Next


References


Summary

A Kubernetes multi-container Pod co-locates tightly coupled containers on one node and gives them a shared network namespace. Each container still has its own state and can restart independently. The native sidecar lab showed the current pattern: content-writer under initContainers with restartPolicy: Always updates index.html on a shared emptyDir volume while nginx serves it on localhost. A startupProbe gates application startup until the sidecar has written the first file.

Adapter and ambassador patterns specialise that idea — transform output on a shared volume, or proxy external traffic through localhost. Regular helpers live in spec.containers; native sidecars use initContainers with restartPolicy: Always when the helper must start first, keep running during the app lifetime, and stop after the application finishes.

Co-locate containers when they must share files or localhost and release together. Split them into separate Pods when scaling or lifecycle independence matters. When logs or files look wrong, check the container name flag, shared volume mounts, and which process owns each port before debugging the application itself.


Frequently Asked Questions

1. What is a sidecar container in Kubernetes?

A sidecar is a helper container in the same Pod as the main application. It extends or supports the app through shared localhost networking or a shared volume without changing the primary container image.

2. How do containers in the same Pod communicate?

They share one network namespace, so processes can reach each other on localhost with different ports. For files, both containers must mount the same Kubernetes volume at their own paths.

3. What is the difference between a sidecar and an init container?

A regular helper under spec.containers runs alongside the application for the Pod lifetime. A native sidecar is declared under spec.initContainers with restartPolicy Always, starts before application containers, and is stopped after they finish.

4. When should I use multiple containers in one Pod?

Use one Pod when containers must be scheduled and removed together, communicate frequently over localhost, or share the same files. Use separate Pods when components scale or release independently.

5. How do I view logs from one container in a multi-container Pod?

Use kubectl logs -c . Add --all-containers=true to print logs from every container in the Pod.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)