Sidecar Containers (Optional)
Sidecars are helper containers that run in the same Pod as your main app, handling operational concerns independently.
- Status
- stable
- Trust
- unverified
- Approved
- ag-archive/0.1.0 ·
You've deployed your agent to Kubernetes. Requests arrive. Your agent handles them. But where do the logs go? Where's the monitoring data? How do you trace which request took how long?
Traditional single-container deployments mix your application logic with logging, monitoring, and debugging concerns. Your agent code becomes cluttered. Operational concerns become tangled with business logic. Testing gets harder.
Sidecars solve this. A sidecar is a helper container that runs alongside your main application container—in the same Pod, sharing the same network and storage. Your agent focuses on its job. The sidecar handles logging, metrics, proxying, or security concerns independently. They coordinate through shared volumes and localhost networking.
This lesson teaches you to design and deploy sidecars using Kubernetes' native sidecar support (available since Kubernetes 1.28). By the end, you'll deploy an agent with a logging sidecar and a metrics sidecar, keeping operational concerns separated from application logic.
The Sidecar Pattern: Separation of Concerns
The Problem: Tangled Concerns
Imagine your FastAPI agent needs to:
- Handle incoming requests
- Write structured logs to a file
- Expose Prometheus metrics
- Trace request latency
If all this logic lives in one container, your code becomes messy:
@app.post("/inference")
async def inference(request: Request):
# Business logic
result = model.predict(request.text)
# Logging concern
with open("/var/log/requests.log", "a") as f:
f.write(json.dumps({"request": request.text, "result": result}))
# Metrics concern
inference_counter.inc()
inference_latency.observe(time.time() - start)
# Return result
return {"prediction": result}Your application code is entangled with operational concerns. Testing requires mocking file I/O and metrics. Scaling requires coordinating all concerns together.
The Solution: Sidecars
Instead, separate concerns into independent containers:
┌──────────────────────────────────────────────────┐
│ Pod (Shared Network) │
├──────────────────────┬──────────────────────────┤
│ │ │
│ Main Container │ Logging Sidecar │
│ (FastAPI Agent) │ │
│ │ - Reads /var/log/agent │
│ @app.post("/") │ - Streams to stdout │
│ Handle requests │ - Or sends to central │
│ │ logging system │
│ │ │
├──────────────────────┼──────────────────────────┤
│ Metrics Sidecar │ Proxy Sidecar │
│ │ (Optional) │
│ - Scrapes metrics │ │
│ - Exposes on :9090 │ - TLS termination │
│ │ - Service mesh inject │
│ │ - Request routing │
└──────────────────────┴──────────────────────────┘
Shared Volume (/var/log)
Shared Network (localhost)Your agent writes logs to /var/log/agent/requests.log. The logging sidecar watches that file and streams it to a central logging service. Your agent exposes metrics on localhost:8000/metrics. A metrics sidecar scrapes and forwards those metrics.
Each container has one job. Testing is simpler. Scaling is predictable.
Native Sidecars: Kubernetes 1.28+
Before Kubernetes 1.28, sidecars were regular containers in the containers field, but Kubernetes couldn't distinguish them from the main application container. This created ambiguity about startup ordering and lifecycle.
Kubernetes 1.28 introduced the initContainers field with a new restartPolicy: Always option specifically for sidecars:
How It Works
- Init containers with
restartPolicy: Alwaysstart before main containers - They run continuously (unlike traditional init containers that exit after setup)
- If a sidecar crashes, Kubernetes restarts it independently
- Main container startup is NOT blocked by sidecar readiness
This guarantees:
- Sidecars are ready before main application starts processing requests
- Sidecars stay alive throughout the Pod's lifetime
- Main container failures don't restart sidecars unnecessarily
Concept 1: Writing Logs to Shared Volumes
Your agent and sidecars must communicate. The most reliable method: shared volumes.
Create a Pod with:
- Main container: Writes logs to
/var/log/agent/ - Logging sidecar: Reads from
/var/log/agent/, streams to stdout or external service
Step 1: Create Agent with Log Volume
Create agent-with-logging.yaml:
apiVersion: v1
kind: Pod
metadata:
name: agent-with-logging
spec:
containers:
- name: agent
image: myregistry.azurecr.io/agent:v1
ports:
- containerPort: 8000
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
env:
- name: LOG_FILE
value: /var/log/agent/requests.log
initContainers:
- name: logging-sidecar
image: fluent/fluent-bit:latest
restartPolicy: Always # <-- Native sidecar: Always restarts
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
args:
- -c
- /fluent-bit/etc/fluent-bit.conf
volumes:
- name: log-volume
emptyDir: {}Key points:
- emptyDir: Temporary storage, shared across containers in the Pod
- volumeMounts: Both containers mount the same volume at
/var/log/agent - initContainers with restartPolicy: Always: Logging sidecar starts before agent and stays running
Deploy it:
kubectl apply -f agent-with-logging.yamlOutput:
pod/agent-with-logging createdVerify the Pod is running:
kubectl get podsOutput:
NAME READY STATUS RESTARTS AGE
agent-with-logging 2/2 Running 0 5sThe 2/2 shows both containers are ready. Without the sidecar, you'd see 1/1.
Step 2: Verify Log Volume Sharing
Exec into the agent container and create a test log:
kubectl exec -it agent-with-logging -c agent -- shInside the container:
echo '{"request": "test", "result": "success"}' >> /var/log/agent/requests.log
cat /var/log/agent/requests.logOutput:
{"request": "test", "result": "success"}Exit:
exitNow exec into the logging sidecar and read the same file:
kubectl exec -it agent-with-logging -c logging-sidecar -- shInside the sidecar:
cat /var/log/agent/requests.logOutput:
{"request": "test", "result": "success"}Both containers see the same logs. The sidecar can now stream this data to a centralized logging service (e.g., Elasticsearch, Splunk, or Stackdriver).
Concept 2: Multi-Sidecar Pod with Metrics
Most production Pods run multiple sidecars. Let's add a metrics sidecar alongside logging.
Create agent-with-logging-and-metrics.yaml:
apiVersion: v1
kind: Pod
metadata:
name: agent-with-sidecars
spec:
containers:
- name: agent
image: myregistry.azurecr.io/agent:v1
ports:
- containerPort: 8000
name: http
- containerPort: 8001
name: metrics # <-- Agent exposes metrics here
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
env:
- name: LOG_FILE
value: /var/log/agent/requests.log
initContainers:
- name: logging-sidecar
image: fluent/fluent-bit:latest
restartPolicy: Always
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
- name: metrics-sidecar
image: prom/prometheus:latest
restartPolicy: Always
ports:
- containerPort: 9090
name: prometheus
volumeMounts:
- name: prometheus-config
mountPath: /etc/prometheus
args:
- --config.file=/etc/prometheus/prometheus.yml
volumes:
- name: log-volume
emptyDir: {}
- name: prometheus-config
configMap:
name: prometheus-configDeploy:
kubectl apply -f agent-with-logging-and-metrics.yamlOutput:
pod/agent-with-sidecars createdCheck Pod status:
kubectl get podsOutput:
NAME READY STATUS RESTARTS AGE
agent-with-sidecars 3/3 Running 0 8sThree containers running in one Pod: agent, logging sidecar, metrics sidecar. All share the Pod's network namespace, so the metrics sidecar can reach the agent's metrics endpoint at localhost:8001/metrics without any network configuration.
Concept 3: Sidecar Lifecycle and Ordering
Init Containers with restartPolicy: Always
When you use initContainers with restartPolicy: Always, Kubernetes guarantees:
- Startup order: Init containers start BEFORE main containers
- Independence: Each init container runs to completion before the next starts
- Restart behavior: If an init container crashes, it restarts (not the entire Pod)
- Main container waits: Main containers don't start until all init containers are healthy
This is different from:
- Traditional init containers (
restartPolicy: OnFailureor default): Run once, exit, never restart - Regular containers: Can be restarted independently, but don't block main container startup
Example: Startup Sequence
Time 0: Pod created
└─ Init container 1 (logging-sidecar) starts
Time 1: Logging sidecar ready
└─ Init container 2 (metrics-sidecar) starts
Time 2: Metrics sidecar ready
└─ Main container (agent) starts
Time 3: Agent running, processing requests
└─ All sidecars handle logging/metrics independently
Time 10: Metrics sidecar crashes
└─ Kubernetes immediately restarts it
└─ Agent continues running (unaffected)
Time 15: Metrics sidecar recovered
└─ Continues scraping metrics from agentThis ordering prevents race conditions where the agent starts before sidecars are ready.
Shutdown Sequence
When a Pod terminates:
Time 0: Pod receives termination signal
└─ All containers receive SIGTERM
Time 0-30: Containers have grace period to shut down
└─ Agent stops accepting requests
└─ Sidecars flush any buffered data
└─ Connections close gracefully
Time 30: Grace period expires
└─ Kubernetes sends SIGKILL
└─ Pod and all containers terminatedAll containers terminate together, which is why sidecars (not separate sidecar Pods) are essential—they share the Pod's lifecycle.
Practical Configuration: Logging Sidecar for Agent Inference
Let's build a realistic logging sidecar for your agent. Your agent logs inference requests and latencies. The sidecar streams these to stdout for collection.
Create agent-inference-logging.yaml:
apiVersion: v1
kind: Pod
metadata:
name: agent-inference-logger
spec:
containers:
- name: agent
image: myregistry.azurecr.io/agent:v1
ports:
- containerPort: 8000
volumeMounts:
- name: logs
mountPath: /var/log/agent
env:
- name: LOG_FILE
value: /var/log/agent/inference.log
initContainers:
- name: log-collector
image: fluent/fluent-bit:latest
restartPolicy: Always
volumeMounts:
- name: logs
mountPath: /var/log/agent
- name: fluent-config
mountPath: /fluent-bit/etc
args:
- -c
- /fluent-bit/etc/fluent-bit.conf
volumes:
- name: logs
emptyDir: {}
- name: fluent-config
configMap:
name: fluent-bit-configDeploy:
kubectl apply -f agent-inference-logging.yamlOutput:
pod/agent-inference-logger createdView logs from the sidecar:
kubectl logs agent-inference-logger -c log-collectorOutput:
[2025-12-22 14:35:12.123] [info] [fluent-bit] version 2.1.0, commit hash: abc1234
[2025-12-22 14:35:12.234] [info] starting log collection from /var/log/agent/inference.log
[2025-12-22 14:35:23.456] [info] collected: {"request_id": "abc123", "latency_ms": 245, "status": "success"}
[2025-12-22 14:35:45.789] [info] collected: {"request_id": "def456", "latency_ms": 198, "status": "success"}The sidecar reads and streams logs, which Kubernetes collects and makes available via kubectl logs.
Try With AI
Open a terminal and work through these scenarios with an AI assistant's help:
Scenario 1: Design a Logging Sidecar
Your task: Design a Pod manifest where:
- Main container: A Python FastAPI agent running on port 8000
- Sidecar: Fluent Bit collecting logs from
/var/log/agent/requests.logand forwarding to an external logging service (e.g., Datadog or CloudWatch)
Ask AI: "Create a Pod manifest with a FastAPI agent and a Fluent Bit sidecar that collects logs and forwards them to [your logging service]."
Review AI's response:
- Is the sidecar configured as an init container with
restartPolicy: Always? - Do both containers mount the same log volume?
- Is the Fluent Bit configuration correct for your logging service?
- Are environment variables set correctly for API keys or endpoints?
Tell AI: "The logging service requires authentication. I need to pass credentials securely."
Ask: "Update the manifest to inject credentials using a Kubernetes Secret."
Reflection:
- How does the sidecar access the Secret?
- What would break if the logging service becomes unavailable?
- Could you test this locally with Docker Desktop Kubernetes?
Scenario 2: Multi-Sidecar Deployment
Your task: Your agent now needs:
- Logging sidecar (Fluent Bit)
- Metrics sidecar (Prometheus)
- Security sidecar (mTLS termination with Linkerd or Istio)
Ask AI: "Create a Pod manifest with three sidecars: logging, metrics, and security. Explain the startup and shutdown ordering."
AI might suggest:
- All sidecars as init containers with
restartPolicy: Always - Separate ConfigMaps for each sidecar's configuration
- Shared volumes for log files, shared network for metrics scraping
Ask: "What happens if the metrics sidecar crashes? Will my agent stop handling requests?"
Reflection:
- Why are sidecars better than separate Pods?
- How would you test sidecar failures locally?
- What's the memory and CPU cost of adding a third sidecar?
Scenario 3: Troubleshoot Sidecar Failures
Your task:
You deployed a Pod with an agent and logging sidecar. The Pod shows 2/2 READY, but logs aren't being collected. The Fluent Bit sidecar is healthy, but no data is appearing in your logging service.
Ask AI: "My logging sidecar is running but not collecting logs. The agent container is writing to /var/log/agent/requests.log. What could be wrong?"
AI might suggest:
- Verify the volume mount path is correct
- Check Fluent Bit configuration syntax
- Confirm the logging service credentials are valid
- Inspect Fluent Bit logs with
kubectl logs -c logging-sidecar
Ask: "Show me the exact kubectl commands to debug this."
Reflection:
- What would you look for in the sidecar logs?
- How would you verify the agent is actually writing to the shared volume?
- Could a permissions issue prevent the sidecar from reading the log file?
Core Concept
Sidecars are helper containers that run in the same Pod as your main app, handling operational concerns independently. Your agent focuses on business logic; the logging sidecar ships logs to a central system, the metrics sidecar scrapes metrics, the proxy sidecar handles TLS. Kubernetes 1.28+ native sidecars use initContainers with restartPolicy: Always—they start before the app but keep running.
Key Mental Models
- Separation of concerns—Application code doesn't know about logging infrastructure. Sidecar watches log files and ships them. Both improve independently. Testing is simpler
- Same Pod, same network—Agent listens on localhost:8000, metrics sidecar scrapes from localhost:8000/metrics. No service discovery, no network latency—direct localhost communication
- Lifecycle coupling—When Pod terminates, all containers (app + sidecars) terminate together. Sidecars never outlive the app. This is stronger than separate Pods where lifecycles are independent
- Native sidecars vs old pattern—Kubernetes 1.28+ allows
initContainerswithrestartPolicy: Alwaysto act as proper sidecars. Before that, sidecars were regular containers incontainerslist, which caused ambiguity
Critical Patterns
- Logging sidecar pattern—App writes logs to /var/log/, sidecar (fluent-bit, logstash) reads and ships to central system. App doesn't care where logs go
- Metrics sidecar pattern—App exposes metrics on localhost:9090, Prometheus sidecar scrapes them every 15 seconds. Metrics stay local to Pod until sidecar forwards them
- Security sidecar pattern—Sidecar handles mTLS termination, request routing, rate limiting. App only handles business logic
- READY count in kubectl get pods—With sidecars, you see
2/2 READY(app + sidecar) instead of1/1. Both must be healthy
Common Mistakes
- Confusing sidecar (always-running helper) with init container (one-time setup)—Sidecars use
restartPolicy: Always - Putting unrelated services in one Pod (sidecars are for operational helpers; use separate Pods for functional microservices)
- Assuming sidecar failures crash the app (they don't; sidecar restarts independently, app keeps running)
- Resource limits on sidecars (easy to forget; sidecar logging can use significant CPU/memory)
Connections
- Builds on: Pods (Lesson 3), init containers (Lesson 6)—sidecars are often configured as init containers with different restart policy
- Leads to: Multi-container Pod architectures, observability patterns for AI agents, security patterns in Lesson 8+ namespaces