A core tenet of containerization is the Single Responsibility Principle: a container should do one thing and do it well. However, applications often require peripheral operations such as logging, monitoring, caching, database connection pooling, or configuration syncing.
Rather than coupling these utility services directly into the main application codebase, Kubernetes allows you to deploy multiple, co-located containers inside a single Pod. This architectural model is known as the Sidecar Container Pattern.
graph TD
subgraph Pod: webserver
direction TB
subgraph nginx-container [nginx-container]
App[Nginx Web Server]
end
subgraph sidecar-container [sidecar-container]
LogShipper[Ubuntu Log Shipper]
end
sharedLogs[("emptyDir Volume <br> (shared-logs)")]
App -->|Writes logs to /var/log/nginx| sharedLogs
sharedLogs <-->|Reads logs from /var/log/nginx| LogShipper
end
LogShipper -->|Streams to stdout| User["kubectl logs -c sidecar-container"]
Containers running in the same Pod share crucial namespaces that make sidecar communication fast and lightweight:
localhost (e.g., the main container can access a local cache container on localhost:6379).emptyDir) to share file directory access in real-time.The Sidecar Pattern involves deploying a secondary helper container (the “sidecar”) alongside the primary application container within a single Pod. The sidecar container enhances or extends the primary container’s functionality without the primary container even being aware of its presence.
The main application container writes its logs to files in a shared volume. A sidecar container running a lightweight log shipper (like Filebeat, Fluent Bit, or a custom script) tails those log files and sends them to centralized log storage (e.g., Elasticsearch, Splunk, Loki). This keeps the main application free from the overhead of log transportation network calls.
Service Meshes (like Istio or Linkerd) inject a sidecar proxy (like Envoy) next to every application container. The proxy intercepts all incoming and outgoing network traffic, handling mutual TLS (mTLS), circuit breaking, routing rules, and metrics collection transparently.
A sidecar container runs a process that watches a Git repository or a configuration service (like HashiCorp Consul). When configuration changes occur, the sidecar downloads the updated files and writes them to a shared volume, where the main application container picks them up without requiring a container restart.
Historically, sidecars were configured as standard containers in the containers array of a Pod. This caused two major lifecycle issues:
Completed state.Starting in Kubernetes 1.28, you can designate an initContainer as a native sidecar by adding the restartPolicy: Always field to its specification.
Kubernetes handles these native sidecars differently:
# Example Native Sidecar Syntax (K8s 1.28+)
spec:
initContainers:
- name: log-shipper
image: fluent-bit:latest
restartPolicy: Always # Defines it as a native sidecar container
thordefaultwebservershared-logs (uses emptyDir)nginx-containernginx:latest/var/log/nginxsidecar-containerubuntu:latest/var/log/nginx["sh", "-c", "while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]Establish connection to the cluster control terminal:
ssh thor@jump_host_ip
Create a new file named sidecar-pod.yaml containing the specifications for the main Nginx container and the log-shipping sidecar container:
apiVersion: v1
kind: Pod
metadata:
name: webserver
labels:
app: web-logging
spec:
volumes:
- name: shared-logs
emptyDir: {}
containers:
- name: nginx-container
image: nginx:latest
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
- name: sidecar-container
image: ubuntu:latest
command: ["sh", "-c", "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
Apply the manifest to initialize the Pod inside the cluster:
kubectl apply -f sidecar-pod.yaml
Expected Output:
pod/webserver created
Wait until the Pod status is reported as Running:
kubectl get pods
Expected Output showing both containers initialized:
NAME READY STATUS RESTARTS AGE
webserver 2/2 Running 0 10s
Send an HTTP request directly to Nginx using curl inside the Nginx container to populate /var/log/nginx/access.log with data:
kubectl exec -it webserver -c nginx-container -- curl http://localhost
Query the logs from the sidecar-container. The sidecar executes a cat command inside an infinite loop, showing log traffic output:
kubectl logs webserver -c sidecar-container
Expected Output containing the curl request details:
127.0.0.1 - - [18/Jul/2026:14:28:10 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.74.0" "-"
The sidecar container successfully monitors and outputs the main Nginx application’s log files from the shared volume!