Skip to main content
Navigation
HomeTechnical ReferenceJournalGitHubGitHub
Sidebar — toggle document categories via the logo
Categories

Kubernetes Common Design Patterns

Overview

Kubernetes provides powerful primitives that enable common distributed-system design patterns without custom code. This reference catalogs the most useful patterns — from multi-container pod compositions to resource management and availability strategies.

Multi-container pod patterns

Sidecar

A helper container that augments the main application — logging, proxying, configuration reloading.

apiVersion: v1
kind: Pod
metadata:
name: app-with-sidecar
labels:
app: app
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: logs
mountPath: /var/log/app
- name: log-shipper
image: fluentd:latest
volumeMounts:
- name: logs
mountPath: /var/log/app
readOnly: true
env:
- name: LOG_DESTINATION
value: "elasticsearch.logging.svc:9200"
volumes:
- name: logs
emptyDir: {}

Ambassador

A proxy container that handles network connections on behalf of the main app — service mesh sidecar, database proxy.

apiVersion: v1
kind: Pod
metadata:
name: app-with-ambassador
labels:
app: app
spec:
containers:
- name: app
image: myapp:latest
env:
- name: CACHE_HOST
value: "localhost" # Connect through ambassador
- name: CACHE_PORT
value: "6379"
- name: redis-ambassador
image: envoyproxy/envoy:latest
ports:
- containerPort: 6379
volumeMounts:
- name: envoy-config
mountPath: /etc/envoy
volumes:
- name: envoy-config
configMap:
name: redis-ambassador-config

Adapter

Transforms application output (metrics, logs) to a standard interface that monitoring tools expect.

apiVersion: v1
kind: Pod
metadata:
name: app-with-metrics-adapter
labels:
app: app
spec:
containers:
- name: app
image: myapp:latest
- name: metrics-adapter
image: prometheus-nginxlog-exporter:latest
ports:
- containerPort: 9113
name: metrics
args:
- -format=json
- -namespace=nginx
volumeMounts:
- name: logs
mountPath: /var/log/nginx
readOnly: true
volumes:
- name: logs
emptyDir: {}

Init containers

Run before the main container starts — schema migrations, secret fetching, preconditions.

apiVersion: v1
kind: Pod
metadata:
name: app-with-init
labels:
app: app
spec:
initContainers:
- name: check-db
image: postgres:16-alpine
command: ["sh", "-c", "until pg_isready -h db -U app -d appdb; do sleep 2; done"]
- name: migrate
image: myapp-migrate:latest
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-creds
key: url
containers:
- name: app
image: myapp:latest

Health checks

Liveness vs Readiness vs Startup

Kubernetes runs three kinds of probes for different purposes: liveness restarts deadlocked containers, readiness removes containers from Service traffic until they're able to serve, and startup protects slow-initializing apps from being killed by the liveness probe before they finish booting. This example configures all three.

apiVersion: v1
kind: Pod
metadata:
name: app-with-health-checks
labels:
app: app
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 3000
# Startup probe: delays liveness until app is ready (protects slow-starting apps)
startupProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 30 # 30 * 5s = 150s max startup time
# Liveness: is the app alive? Restart if failing.
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 0
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
# Readiness: is the app ready to serve traffic? Remove from service if failing.
readinessProbe:
httpGet:
path: /ready
port: 3000
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3

Resource management

Requests and limits

Requests and limits tell the scheduler how much CPU and memory a container needs and how much it may use. Requests guarantee the minimum and inform pod placement; limits cap usage, throttling CPU and OOM-killing containers that exceed their memory.

apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: app
spec:
replicas: 3
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: myapp:latest
resources:
requests: # What the container is guaranteed
cpu: "250m"
memory: "256Mi"
limits: # What the container may burst to
cpu: "1000m"
memory: "512Mi"
SettingEffect
requestsMinimum guaranteed resources. Used by the scheduler for placement.
limitsHard cap. CPU throttling above limit; OOMKill for memory above limit.

Best practices:

  • Always set requests. They inform the scheduler and prevent node overcommit.
  • Set limits for memory (OOM is worse than throttling).
  • CPU limits are debatable — some teams omit them and rely on requests.

Quality of Service (QoS) classes

ClassRequests?Limits?Priority for eviction
GuaranteedRequests = Limits (both set)Lowest
BurstableRequests set, Limits not equalMedium
BestEffortNeither setHighest (evicted first)

Pod disruption budgets (PDB)

PDBs protect against voluntary disruptions (node drains, cluster autoscaler):

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 2 # At least 2 pods must be running
# OR
maxUnavailable: 1 # At most 1 pod may be unavailable
selector:
matchLabels:
app: my-app

Scheduling and affinity

Pod anti-affinity (spread across nodes)

Anti-affinity spreads replicas across different nodes so that a single node failure doesn't take down every copy. This example prefers spreading my-app pods across hosts — the topologyKey defines the spread domain, here the node.

apiVersion: v1
kind: Pod
metadata:
name: app
labels:
app: my-app
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: my-app
topologyKey: kubernetes.io/hostname
containers:
- name: app
image: myapp:latest

Node affinity

Node affinity constrains which nodes a pod can land on — for example, only instance types with enough resources. requiredDuringSchedulingIgnoredDuringExecution is a hard constraint the scheduler must satisfy, while preferredDuringScheduling... is a soft preference it tries to honor.

apiVersion: v1
kind: Pod
metadata:
name: app
labels:
app: app
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values: ["t3.medium", "t3.large"]
containers:
- name: app
image: myapp:latest

Topology spread constraints

Spread constraints distribute pods evenly across a domain such as zones or hosts, balancing more predictably than affinity rules. maxSkew caps how far any two domains can diverge in pod count, and whenUnsatisfiable: DoNotSchedule makes the constraint hard.

apiVersion: v1
kind: Pod
metadata:
name: app
labels:
app: my-app
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: my-app
containers:
- name: app
image: myapp:latest

Taints and tolerations

Nodes repel pods that don't tolerate their taints:

kubectl taint nodes worker-1 dedicated=database:NoSchedule
apiVersion: v1
kind: Pod
metadata:
name: app
labels:
app: app
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "database"
effect: "NoSchedule"
containers:
- name: app
image: myapp:latest

See also