8.25 Kubernetes Pod Design Practice
Kubernetes Pod Design Practice covers best practices for structuring pods to ensure reliability, scalability, and efficient resource use in Kubernetes.
Kubernetes Pod Design Practice is the body of conventions and architectural decisions applied when structuring workloads into Pods, governing how many containers a Pod should include, how responsibilities are divided among them, and how the resulting design affects scalability, resilience, and operability of the running application. These practices have emerged from years of production experience and shape most of the higher-level guidance found in Kubernetes documentation and tooling.
Single Responsibility Per Pod
One Application Instance Per Pod
The most fundamental design practice treats a Pod as representing a single logical instance of an application, rather than bundling multiple unrelated applications together. This allows the Pod to be scaled, scheduled, and replaced as an independent unit, since Kubernetes scaling primitives such as ReplicaSets operate at the granularity of whole Pods, not individual containers within them.
Avoiding Multiple Unrelated Processes in One Container
Within a single container, running multiple unrelated processes is generally discouraged in favor of one primary process per container, since Kubernetes health checks, resource limits, and logging are all scoped to the container as a unit, and bundling unrelated processes obscures which specific process is responsible for a failure or resource spike.
Multi-Container Pod Patterns
Sidecar Pattern
The sidecar pattern places a secondary container alongside the main application container to extend or enhance its behavior, such as a logging agent that tails and forwards log files, or a service mesh proxy that manages inbound and outbound network traffic. The sidecar shares the Pod's network and, optionally, its storage volumes with the primary container.
Ambassador Pattern
The ambassador pattern uses a secondary container as a local proxy that simplifies how the main application connects to external services, abstracting away service discovery, retries, or protocol translation so the primary container can communicate with a fixed local address rather than handling that complexity itself.
Adapter Pattern
The adapter pattern uses a secondary container to standardize or transform the output of the main application into a format expected by external tooling, such as converting an application's native metrics format into one compatible with a monitoring system, without modifying the application container itself.
Init Containers in Pod Design
Separating Setup From Runtime Logic
Init containers run to completion before any application container starts, and are commonly used to encapsulate one-time setup tasks such as database schema migrations, waiting for a dependency to become available, or populating a volume with configuration files, keeping this logic cleanly separated from the long-running application logic.
Sequential Execution Guarantees
Because init containers execute sequentially and each must complete successfully before the next begins, they provide a predictable ordering guarantee that is useful for expressing setup steps with dependencies on one another, something that cannot be reliably expressed among containers running concurrently within the same Pod.
Resource and Probe Configuration as Design Elements
Defining Requests and Limits Deliberately
Sound Pod design practice includes deliberately setting resources.requests and resources.limits for every container, rather than omitting them, since unset requests can lead to unpredictable scheduling behavior and unset limits can allow a single container to consume disproportionate node resources.
Layering Liveness, Readiness, and Startup Probes
Well-designed Pods typically configure all three probe types where appropriate: a startup probe to accommodate slow initialization, a liveness probe to detect and recover from deadlocks or unrecoverable internal states, and a readiness probe to control traffic admission independently of whether the process is merely alive.
Volume and Configuration Separation
Externalizing Configuration From Images
Established practice separates configuration from the container image itself, injecting configuration through ConfigMaps and Secrets mounted as volumes or environment variables, so that the same image can be reused across environments without rebuilding it for each configuration variant.
Scoped Volume Sharing Between Containers
When containers within a Pod need to share data, such as a main application writing files that a sidecar consumes, volumes such as emptyDir provide a scoped, Pod-lifetime-bound sharing mechanism, avoiding the need for containers to communicate through the network for what is fundamentally local, ephemeral data exchange.
Avoiding Anti-Patterns
Overloaded Pods
A common anti-pattern is packing many unrelated services into a single Pod purely to reduce the number of objects managed, which undermines independent scaling, complicates rolling updates, and ties the failure domains of otherwise unrelated components together unnecessarily.
Ignoring Pod Disruption Considerations
Neglecting to define a PodDisruptionBudget or appropriate replica counts for workloads that must maintain availability during voluntary disruptions, such as node maintenance, is a design oversight that can result in unexpected downtime despite the application otherwise being architected correctly.