DEV Community

janak0ff
janak0ff

Posted on

Day 55: Kubernetes Sidecar Containers

We have a web server container running the nginx image. The access and error logs generated by the web server are not critical enough to be placed on a persistent volume. However, Nautilus developers need access to the last 24 hours of logs so that they can trace issues and bugs. Therefore, we need to ship the access and error logs for the web server to a log-aggregation service. Following the separation of concerns principle, we implement the Sidecar pattern by deploying a second container that ships the error and access logs from nginx. Nginx does one thing, and it does it well - serving web pages. The second container also specializes in its task - shipping logs. Since containers are running on the same Pod, we can use a shared emptyDir volume to read and write logs.

  1. Create a pod named webserver.
  2. Create an emptyDir volume named shared-logs.
  3. Create a regular container in the webserver pod from the nginx:latest image named nginx-container, and an init container from the ubuntu:latest image named sidecar-container.
  4. Add the following command to the sidecar-container "sh","-c","while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"
  5. Mount the shared-logs volume in both containers at /var/log/nginx. Ensure all containers are in a running state.

What is a Sidecar Container?

Think of a sidecar like a motorcycle sidecar – it's attached to the main vehicle and extends its capabilities without changing the main vehicle itself.

┌─────────────────────────────────────────────────────────────────────────────┐
│                    The Sidecar Analogy                                     │
│                                                                              │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │  Motorcycle: The Main Vehicle                                         │ │
│  │  - Does its primary job (serving web pages)                           │ │
│  │  - Doesn't worry about extra tasks                                    │ │
│  └────────────────────────────────────────────────────────────────────────┘ │
│                                    │                                         │
│                                    ▼                                         │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │  Sidecar: Adds Extra Functionality                                    │ │
│  │  - Extends capabilities                                               │ │
│  │  - Doesn't change the motorcycle                                      │ │
│  │  - Can be attached/removed                                            │ │
│  └────────────────────────────────────────────────────────────────────────┘ │
│                                                                              │
│  In Kubernetes, the "motorcycle" is your main container,                    │
│  and the "sidecar" is an additional container that enhances it.            │
└─────────────────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

📋 What We'll Build Today

Our Task:

  • Pod Name: webserver
  • Main Container: nginx-container (nginx:latest) – serves web pages
  • Sidecar Container: sidecar-container (ubuntu:latest) – ships logs
  • Shared Volume: shared-logs (emptyDir)
  • Mount Path: /var/log/nginx (both containers)

Why Sidecar?

Instead of modifying Nginx to handle log shipping, we add a separate container that specializes in this task. This follows the Separation of Concerns principle.


📖 Understanding the Sidecar Pattern

The Problem

Nginx generates logs, but we need to ship them to a log aggregator. Two approaches:

Approach Description Pros Cons
Modify Nginx Add log shipping code to Nginx Simple Violates single responsibility
Sidecar Pattern Add separate log shipping container Clean separation, easy to update More containers

The Sidecar Solution

┌─────────────────────────────────────────────────────────────────────────────┐
│                      Sidecar Pattern Architecture                          │
│                                                                              │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │                    Pod: webserver                                     │ │
│  │                                                                       │ │
│  │  ┌──────────────────────────────┐  ┌──────────────────────────────┐  │ │
│  │  │  Main Container              │  │  Sidecar Container           │  │ │
│  │  │  nginx-container             │  │  sidecar-container           │  │ │
│  │  │                              │  │  (Init Container)            │  │ │
│  │  │  - Serves web pages          │  │  restartPolicy: Always       │  │ │
│  │  │  - Writes logs to shared     │  │  - Reads logs                │  │ │
│  │  │    volume                    │  │  - Ships logs to aggregator  │  │ │
│  │  │                              │  │  - Does NOT modify logs      │  │ │
│  │  │                              │  │                              │  │ │
│  │  │  Writes to:                  │  │  Reads from:                 │  │ │
│  │  │  /var/log/nginx/access.log   │◄─┤  /var/log/nginx/access.log   │  │ │
│  │  │  /var/log/nginx/error.log    │◄─┤  /var/log/nginx/error.log    │  │ │
│  │  └──────────────────────────────┘  └──────────────────────────────┘  │ │
│  │                    ▲                      ▲                         │ │
│  │                    │                      │                         │ │
│  │  ┌─────────────────┴──────────────────────┴──────────────────────┐  │ │
│  │  │              Volume: shared-logs (emptyDir)                   │  │ │
│  │  │              Mounted at: /var/log/nginx                       │  │ │
│  │  └────────────────────────────────────────────────────────────────┘  │ │
│  └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

🔧 Step-by-Step Guide

Step 1: Delete Existing Pod (if any)

kubectl delete pod webserver --ignore-not-found
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the YAML Manifest

vi webserver.yaml
Enter fullscreen mode Exit fullscreen mode

YAML Content:

apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  volumes:
  - name: shared-logs
    emptyDir: {}
  initContainers:
  - name: sidecar-container
    image: ubuntu:latest
    restartPolicy: Always
    command: ["/bin/sh", "-c"]
    args: ["while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  containers:
  - name: nginx-container
    image: nginx:latest
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
Enter fullscreen mode Exit fullscreen mode

📖 YAML Breakdown

Volume Section

volumes:
- name: shared-logs
  emptyDir: {}
Enter fullscreen mode Exit fullscreen mode
  • name: shared-logs – The volume name
  • emptyDir: {} – Temporary storage that exists for the Pod's lifetime

Sidecar Container (Init Container with restartPolicy: Always)

initContainers:
- name: sidecar-container
  image: ubuntu:latest
  restartPolicy: Always    # ← KEY: Makes it a sidecar!
  command: ["/bin/sh", "-c"]
  args: ["while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]
  volumeMounts:
  - name: shared-logs
    mountPath: /var/log/nginx
Enter fullscreen mode Exit fullscreen mode
  • restartPolicy: Always – This is what makes it a sidecar container!
  • image: ubuntu:latest – Lightweight Linux image
  • command – Infinite loop reading logs every 30 seconds
  • mountPath: /var/log/nginx – Same mount path as nginx

Main Container (Nginx)

containers:
- name: nginx-container
  image: nginx:latest
  volumeMounts:
  - name: shared-logs
    mountPath: /var/log/nginx
Enter fullscreen mode Exit fullscreen mode
  • image: nginx:latest – Web server
  • mountPath: /var/log/nginx – Where nginx writes logs

Step 3: Create the Pod

kubectl apply -f webserver.yaml
Enter fullscreen mode Exit fullscreen mode

Output:

pod/webserver created
Enter fullscreen mode Exit fullscreen mode

Step 4: Verify the Pod

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Output:

NAME        READY   STATUS    RESTARTS   AGE
webserver   1/1     Running   0          10s
Enter fullscreen mode Exit fullscreen mode

Step 5: Verify Both Containers

# Check init container exists
kubectl get pods webserver -o jsonpath='{.status.initContainerStatuses[*].name}'
Enter fullscreen mode Exit fullscreen mode

Output:

sidecar-container
Enter fullscreen mode Exit fullscreen mode
# Check regular container exists
kubectl get pods webserver -o jsonpath='{.status.containerStatuses[*].name}'
Enter fullscreen mode Exit fullscreen mode

Output:

nginx-container
Enter fullscreen mode Exit fullscreen mode
# Check images
kubectl get pods webserver -o jsonpath='{.spec.initContainers[*].image}'
Enter fullscreen mode Exit fullscreen mode

Output:

ubuntu:latest
Enter fullscreen mode Exit fullscreen mode
kubectl get pods webserver -o jsonpath='{.spec.containers[*].image}'
Enter fullscreen mode Exit fullscreen mode

Output:

nginx:latest
Enter fullscreen mode Exit fullscreen mode

Step 6: Generate Traffic

kubectl exec -it webserver -c nginx-container -- curl localhost
Enter fullscreen mode Exit fullscreen mode

Step 7: Check Sidecar Logs

kubectl logs webserver -c sidecar-container
Enter fullscreen mode Exit fullscreen mode

Output:

2026/08/24 06:38:27 [notice] 1#1: using the "epoll" event method
2026/08/24 06:38:27 [notice] 1#1: nginx/1.31.4
...
::1 - - [24/Aug/2026:06:38:40 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.14.1" "-"
Enter fullscreen mode Exit fullscreen mode

Step 8: Verify Shared Volume

# Check in nginx container
kubectl exec -it webserver -c nginx-container -- ls -la /var/log/nginx/
Enter fullscreen mode Exit fullscreen mode

Output:

total 20
drwxrwxrwx 2 root root 4096 Aug 24 06:38 .
drwxr-xr-x 1 root root 4096 Aug 19 19:08 ..
-rw-r--r-- 1 root root  168 Aug 24 06:40 access.log
-rw-r--r-- 1 root root 1077 Aug 24 06:38 error.log
Enter fullscreen mode Exit fullscreen mode
# Check in sidecar container (same files!)
kubectl exec -it webserver -c sidecar-container -- ls -la /var/log/nginx/
Enter fullscreen mode Exit fullscreen mode

Output:

total 16
drwxrwxrwx 2 root root 4096 Aug 24 06:38 .
drwxr-xr-x 1 root root 4096 Aug 24 06:38 ..
-rw-r--r-- 1 root root  168 Aug 24 06:40 access.log
-rw-r--r-- 1 root root 1077 Aug 24 06:38 error.log
Enter fullscreen mode Exit fullscreen mode

🔍 How the Sidecar Command Works

while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done
Enter fullscreen mode Exit fullscreen mode
Part Purpose
while true; do Infinite loop
cat /var/log/nginx/access.log /var/log/nginx/error.log Read both log files
sleep 30 Wait 30 seconds before repeating
done End of loop

What this achieves:

  • ✅ Continuously reads logs every 30 seconds
  • ✅ Outputs logs to stdout (for log aggregation)
  • ✅ Keeps the container running
  • ✅ No modifications to Nginx

📝 Complete Commands Summary

# 1. Delete existing pod
kubectl delete pod webserver --ignore-not-found

# 2. Create the YAML file
cat > webserver.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  volumes:
  - name: shared-logs
    emptyDir: {}
  initContainers:
  - name: sidecar-container
    image: ubuntu:latest
    restartPolicy: Always
    command: ["/bin/sh", "-c"]
    args: ["while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  containers:
  - name: nginx-container
    image: nginx:latest
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
EOF

# 3. Apply the configuration
kubectl apply -f webserver.yaml

# 4. Check pod status
kubectl get pods

# 5. Wait for pod to be ready
kubectl wait --for=condition=ready pod/webserver --timeout=60s

# 6. Verify both containers exist
kubectl get pods webserver -o jsonpath='{.status.initContainerStatuses[*].name}'
kubectl get pods webserver -o jsonpath='{.status.containerStatuses[*].name}'

# 7. Verify images
kubectl get pods webserver -o jsonpath='{.spec.initContainers[*].image}'
kubectl get pods webserver -o jsonpath='{.spec.containers[*].image}'

# 8. Generate traffic
kubectl exec -it webserver -c nginx-container -- curl localhost

# 9. Check sidecar logs
kubectl logs webserver -c sidecar-container

# 10. Verify shared volume
kubectl exec -it webserver -c nginx-container -- ls -la /var/log/nginx/
kubectl exec -it webserver -c sidecar-container -- ls -la /var/log/nginx/
Enter fullscreen mode Exit fullscreen mode

🎯 Key Learnings

1. Sidecar Pattern

  • Extends the main container's functionality without modifying it
  • Follows the Separation of Concerns principle
  • Nginx serves web pages, sidecar ships logs

2. Shared Volume

  • emptyDir volume is created at the Pod level
  • Both containers mount the same volume at the same path (/var/log/nginx)
  • Nginx writes logs, sidecar reads them

3. Sidecar as Init Container with restartPolicy: Always

initContainers:
- name: sidecar-container
  image: ubuntu:latest
  restartPolicy: Always  # ← Makes it a sidecar!
Enter fullscreen mode Exit fullscreen mode
  • Starts before the main container
  • Continues running for the entire Pod lifetime
  • Shares the same volume with the main container

4. Verification

  • kubectl logs with -c flag to check specific container logs
  • kubectl exec to verify shared volume contents

📊 Sidecar Use Cases

Use Case Main Container Sidecar Container
Log Shipping Web server (Nginx) Log shipper (Fluentd)
Configuration Sync App server Config sync agent
Proxying App server Reverse proxy
Monitoring App server Monitoring agent
Database Migration App server Migration runner

🎉 You Did It!

You've successfully implemented the Sidecar pattern in Kubernetes! This is a critical skill for:

  • Log aggregation – Shipping logs to centralized storage
  • Monitoring – Adding monitoring agents
  • Proxying – Adding reverse proxies
  • Configuration – Dynamic configuration updates

📚 Quick Reference

Pod with Sidecar Template

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  initContainers:
  - name: sidecar
    image: sidecar-image:latest
    restartPolicy: Always   # ← Makes it a sidecar!
    volumeMounts:
    - name: shared-data
      mountPath: /data
  containers:
  - name: main-container
    image: main-image:latest
    volumeMounts:
    - name: shared-data
      mountPath: /data
Enter fullscreen mode Exit fullscreen mode

Useful Commands

Command Purpose
kubectl get pods List pods
kubectl logs <pod> -c <container> View container logs
kubectl describe pod <pod> Detailed pod info
kubectl exec -it <pod> -c <container> -- /bin/bash Shell into container

Top comments (0)