The Sidecar Pattern
Adapted from: Bilgin Ibryam & Roland Huß, Kubernetes Patterns (O'Reilly, 2019), Chapter 3 — rewritten and condensed for teaching.
The first single-node pattern is the sidecar pattern: two containers scheduled together on one machine, packaged as an atomic group. In Kubernetes that group is a Pod.
- The application container holds the core logic. Without it, the application does not exist.
- The sidecar container augments the application — often without the application knowing — adding capabilities that would be hard to bolt onto the app directly.
Sidecars are co-scheduled onto the same machine and share resources: parts of the filesystem, the hostname, the network namespace, and other Linux namespaces. That shared context is what makes localhost proxying, shared config volumes, and PID-namespace introspection possible.
flowchart LR
subgraph pod["Pod (single network namespace)"]
SC[Sidecar container]
APP[Application container]
SC -->|"localhost"| APP
end
EXT[External clients] -->|HTTPS| SC
Adding HTTPS to a Legacy Service
Consider a legacy web service built years ago that speaks HTTP only. Security policy now requires HTTPS everywhere, but the source tree only builds on a retired toolchain — resurrecting that build system or porting the app would take months.
Containerizing the binary is easy: run it in a container with an old userspace on a modern kernel. Adding TLS inside the app is the hard part.
The sidecar fix:
- Configure the legacy app to listen on localhost only (
127.0.0.1). Normally that would make it unreachable from outside the machine — but the sidecar shares the same network namespace, so localhost inside the Pod is shared. - Add an nginx sidecar (or any TLS-terminating proxy) that listens on the Pod's external address, terminates HTTPS, and forwards plain HTTP to
127.0.0.1:<app-port>. - Unencrypted traffic never leaves the loopback interface inside the Pod — acceptable to most security reviews — and the team never touches the legacy source.
flowchart LR
CLIENT[Client] -->|HTTPS| NGX[nginx sidecar]
NGX -->|"HTTP on 127.0.0.1"| LEG[Legacy app]
The same shape extends to automatic certificate rotation, or adding authentication/authorization in front of code you cannot modify.
Check: HTTPS sidecar
TLS termination with a sidecar
1 / 2Why must the legacy application bind to localhost (127.0.0.1) in the HTTPS sidecar setup?
Pick an answer to see if you are right.
Practice: validate loopback binding
The legacy app must listen on loopback so only the sidecar in the same network namespace can reach it over plain HTTP. Implement is_loopback_bind(host) — return True for 127.0.0.1, localhost, or ::1, and False otherwise.
Dynamic Configuration with Sidecars
Proxying traffic is not the only use case. Many older applications read a configuration file from disk at startup (plain text, TOML, XML, JSON, or YAML). In a cloud-native environment you usually want configuration pushed through an API — for automation, rollback, and avoiding SSH edits on every server. Kubernetes exposes ConfigMap resources for this.
New apps can call the API directly. Legacy apps cannot — but a configuration sidecar can:
- Share a volume with the application container (often a ConfigMap mount).
- On startup, compare the API's desired config with what is on disk.
- If they differ, write the new file into the shared directory and signal the app (file watch,
SIGHUP, or in extreme casesSIGKILLso the orchestrator restarts the pod and the app reloads on boot).
flowchart TB
API[Config API / ConfigMap]
CM[Config sidecar]
VOL[(Shared volume)]
APP[Application container]
API --> CM
CM --> VOL
APP --> VOL
The notification mechanism varies: some apps watch the file; others reload on SIGHUP; the harshest option kills the app and lets the orchestrator restart it with the new file already in place.
Practice: when should the config sidecar sync?
Implement needs_sync(api_version, file_version) — return True when the versions differ and the sidecar should write an updated config file to the shared volume.
Modular Application Containers
Sidecars are not only for legacy adaptation. They also enable modular, reusable operational tooling.
Example: every service should expose a /topz HTTP endpoint showing process resource usage (like the top command). You could ask every team to embed that in every language — or ship one topz sidecar that shares the application's PID namespace, introspects all processes, and serves a consistent UI. The orchestrator can inject that sidecar into every deployment automatically.
Trade-off: an off-the-shelf sidecar is less tailored than in-process code (like off-the-rack vs bespoke clothing). For most teams, reuse wins; for extreme performance needs, embed the logic in the app.
Piston cannot run Docker or share PID namespaces. On a machine with Docker installed, you can try Brendan Burns' topz image:
docker run -d <my-app-image>
# note the container id as APP_ID
docker run --pid=container:${APP_ID} -p 8080:8080 brendanburns/topz:db0fa58 /server --addr=0.0.0.0:8080
Visit http://localhost:8080/topz to see processes from the application container. Change the published port if your app already uses 8080.
Check: modular sidecars
Modular utility sidecars
1 / 2Why deploy /topz-style monitoring as a sidecar instead of a per-language library inside every application?
Pick an answer to see if you are right.
Building a Simple PaaS with Sidecars
Sidecars can carry application workflow, not just adaptation.
Imagine a minimal PaaS where git push deploys code:
- Application container: a Node.js server using nodemon to reload when files change.
- Sidecar container: shares a filesystem with the app and runs a loop —
git pull, sleep, repeat — keeping the working tree synchronized with a remote repository.
Push to Git → sidecar updates files → nodemon reloads the server. Two containers, one Pod, no changes to the Node.js application code for deployment mechanics.
flowchart LR
GIT[Git remote]
SC[git-sync sidecar]
FS[(Shared filesystem)]
APP[Node.js + nodemon]
GIT --> SC
SC --> FS
FS --> APP
Designing Sidecars for Modularity and Reusability
Reusable sidecars need the same discipline as reusable libraries:
Parameterized containers
Treat each image like a function. Expose inputs that customize a generic image to a specific deployment — e.g. an HTTPS sidecar needs at least certificate path and backend port on localhost. Pass parameters via environment variables (preferred) or command-line flags. A small entrypoint script reads env vars and renders config (nginx.conf, sync interval, etc.).
Define each container's API
Parameters are part of the API, but so are:
- HTTP endpoints the sidecar exposes
- Outbound calls the sidecar makes
- Semantics of each env var (units, defaults, stability)
Renaming UPDATE_FREQUENCY to UPDATE_PERIOD is an obvious breaking change. Subtler breaks matter too: changing seconds to milliseconds without updating callers, or accepting "10m" strings where 10 used to mean ten seconds, can silently change behavior and load on the config API.
Document your containers
Document in the Dockerfile: EXPOSE with comments, ENV defaults with descriptions, and OCI image labels (org.opencontainers.image.vendor, url, version) so community tools can discover metadata consistently.
Practice: validate a sidecar Pod layout
Given a simplified Pod description, decide whether it satisfies the minimum sidecar contract: at least two containers, and at least one volume mount name shared between the app and sidecar (so they can share config or files).
Each container is {"name": str, "volumeMounts": [str, ...]}. Implement is_sidecar_pod(containers).
Check: sidecar API stability
Sidecar APIs and parameters
1 / 2A config sidecar used to treat UPDATE_FREQUENCY=10 as ten seconds. A new release parses bare numbers as milliseconds but still accepts "10s" for seconds. What kind of change is this?
Pick an answer to see if you are right.
- The sidecar pattern pairs an application container with a co-scheduled helper in the same Pod, sharing network, filesystem, and other namespaces.
- HTTPS for legacy apps: bind the app to localhost; terminate TLS in a proxy sidecar on the shared network namespace.
- Dynamic config: a sidecar syncs API/ConfigMap state into a shared volume and signals (or restarts) the legacy app.
- Modular ops: utility sidecars like
/topzreuse one implementation across languages when they share the PID namespace. - PaaS-style workflows: a git-sync sidecar plus a reload-aware app container can implement push-to-deploy without embedding deploy logic in the app.
- Reusable sidecars need parameterized images, a stable API surface (including env var semantics), and clear documentation — treat breaking changes as seriously as library semver.
Further Reading
- Bilgin Ibryam & Roland Huß — Kubernetes Patterns (O'Reilly, 2019), Chapter 3
- Ambassadors — the next lesson: brokering sharded storage, service discovery, and traffic splitting
- Brendan Burns — Designing Distributed Systems (O'Reilly), single-node patterns
- Important Distributed System Concepts — orchestration, liveness, and readiness in this series
- Kubernetes Pod documentation