The 10 Most Important Kubernetes Design Patterns for Production
The most critical Kubernetes design patterns for production workloads are the Health Probe, Predictable Demands, Automated Placement, Init Container, Sidecar, Batch Job, Stateful Service, Service Discovery, Controller, and Operator patterns, which collectively ensure reliability, scalability, and operational automation.
The ByteByteGoHq/system-design-101 repository categorizes these patterns in data/guides/top-10-k8s-design-patterns.md into four distinct groups: Foundational, Structural, Behavioral, and Higher-Level. Mastering these patterns enables engineers to build self-healing systems that efficiently utilize cluster resources while maintaining clear separation of concerns.
Foundational Patterns
Foundational patterns address the basic requirements for running containerized workloads reliably in a Kubernetes cluster. These focus on observability, resource management, and intelligent scheduling.
Health Probe Pattern
The Health Probe Pattern requires exposing livenessProbe and readinessProbe endpoints so the Kubernetes control plane can automatically detect and recover unhealthy containers. According to the source guide, this pattern guarantees self-healing and reduces manual intervention by allowing the kubelet to restart failed containers or remove them from service endpoints.
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- name: app
image: myorg/web-app:latest
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Predictable Demands Pattern
The Predictable Demands Pattern mandates declaring explicit resource requests and limits alongside runtime dependencies in the pod specification. This enables the scheduler to make informed placement decisions and prevents resource contention, ensuring applications receive the CPU and memory they require without starving neighboring workloads.
resources:
requests:
cpu: "500m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
Automated Placement Pattern
The Automated Placement Pattern leverages Kubernetes’ native scheduler (or custom schedulers) to place pods based on constraints, affinity rules, and taints. As implemented in the repository, this ensures optimal utilization and fault-domain isolation by distributing workloads across availability zones and specialized hardware nodes.
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: ["us-east-1a", "us-east-1b"]
Structural Patterns
Structural patterns define how containers are organized within pods to manage initialization, extension, and cross-cutting concerns without modifying the main application.
Init Container Pattern
The Init Container Pattern runs one-off initialization tasks in a separate container that completes before the main app starts. This guarantees that prerequisites—such as database migrations, configuration generation, or secret fetching—succeed before the workload becomes live, ensuring the application starts in a known good state.
initContainers:
- name: migrate-db
image: myorg/db-migrate:latest
command: ["sh", "-c", "python migrate.py"]
envFrom:
- secretRef:
name: db-creds
Sidecar Pattern
The Sidecar Pattern attaches auxiliary containers (for logging, monitoring, or proxying) to the same pod to extend functionality without modifying the main application. This promotes separation of concerns and allows independent updates of cross-cutting infrastructure components while keeping the primary container image focused on business logic.
containers:
- name: app
image: myorg/app:latest
- name: fluent-bit
image: fluent/fluent-bit:latest
volumeMounts:
- name: logs
mountPath: /var/log/app
Behavioral Patterns
Behavioral patterns address specific workload types and communication models, providing abstractions for stateful services, batch processing, and service discovery.
Batch Job Pattern
The Batch Job Pattern models short-lived, isolated units of work as Job or CronJob resources. This provides reliable execution with automatic retries and completion tracking for background processing tasks such as data exports, report generation, or maintenance operations.
apiVersion: batch/v1
kind: Job
metadata:
name: export-data
spec:
template:
spec:
containers:
- name: exporter
image: myorg/exporter:latest
restartPolicy: OnFailure
Stateful Service Pattern
The Stateful Service Pattern utilizes StatefulSet resources with stable network IDs and Persistent Volume Claims (PVCs) for stateful workloads. This guarantees ordered, graceful scaling and data durability for databases, caches, and message queues that require stable identity and persistent storage.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: "redis"
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
Service Discovery Pattern
The Service Discovery Pattern exposes services via Service objects, DNS, or external service mesh implementations, allowing clients to locate endpoints dynamically. This removes hard-coded host and port configuration, supporting rolling updates and horizontal scaling without client reconfiguration.
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 8080
Higher-Level Patterns
Higher-level patterns implement automation and encapsulate operational knowledge to manage complex applications throughout their lifecycle.
Controller Pattern
The Controller Pattern implements a custom controller that watches resources and reconciles the actual state with the desired state. As described in data/guides/top-10-k8s-design-patterns.md, this automates complex operational logic such as custom scaling behaviors or failover procedures across many resources.
type MyAppReconciler struct {
client.Client
Scheme *runtime.Scheme
}
func (r *MyAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// fetch MyApp CR
// compare desired state vs. actual state
// create/update/delete underlying resources
return ctrl.Result{}, nil
}
Operator Pattern
The Operator Pattern embeds domain-specific expertise inside a controller (the "operator") to manage the entire lifecycle of an application. This turns operational knowledge into code, enabling fully automated day-2 operations for databases, message queues, and other complex systems.
apiVersion: myorg.com/v1alpha1
kind: MyDatabase
metadata:
name: example-db
spec:
size: 3
version: "12"
The operator’s controller watches MyDatabase resources and ensures a corresponding StatefulSet with the requested number of replicas exists, upgrades it when spec.version changes, and performs backups on a schedule.
Summary
- Health Probe, Predictable Demands, and Automated Placement form the foundation for reliable, observable, and efficiently scheduled workloads.
- Init Container and Sidecar patterns provide structural flexibility for initialization and cross-cutting concerns without modifying application code.
- Batch Job, Stateful Service, and Service Discovery address specific workload behaviors, ensuring proper handling of ephemeral tasks, persistent state, and dynamic networking.
- Controller and Operator patterns enable higher-level automation by encoding operational expertise into the cluster control plane.
- The ByteByteGoHq/system-design-101 repository provides detailed examples in
data/guides/top-10-k8s-design-patterns.mdalongside complementary guides indata/guides/kubernetes-deployment-strategies.mdanddata/guides/kubernetes-tools-ecosystem.md.
Frequently Asked Questions
What is the difference between the Controller Pattern and the Operator Pattern?
The Controller Pattern is a general mechanism for reconciling desired state with actual state for any Kubernetes resource, while the Operator Pattern is a specific application of controllers that encodes domain-specific operational knowledge (such as database upgrades or backup procedures) into software. According to the source guide, operators are essentially controllers with embedded expertise for managing complex stateful applications.
When should I use a StatefulSet instead of a Deployment?
Use a StatefulSet when your application requires stable network identifiers, persistent storage, or ordered deployment and scaling—common with databases like PostgreSQL or Redis. Deployments are better suited for stateless applications where pods are interchangeable and do not require persistent identity or storage, as they provide simpler rolling updates and scaling.
How do Init Containers differ from Sidecars?
Init Containers run to completion before the main application containers start, making them ideal for setup tasks like database migrations or configuration generation. Sidecars run concurrently with the main application, extending its functionality for concerns like logging, monitoring, or proxying without modifying the primary container code.
Why are resource requests and limits critical for production Kubernetes?
Resource requests inform the scheduler which node has sufficient capacity to run the pod, while limits prevent a single container from consuming excessive CPU or memory and affecting neighboring workloads. As noted in the ByteByteGoHq repository, the Predictable Demands Pattern ensures the scheduler makes informed placement decisions and prevents resource contention in multi-tenant clusters.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →