Kubernetes Container Design Guidelines
Kubernetes Container Design Guidelines provide best practices for building scalable, secure, and efficient containerized applications on Kubernetes.
Kubernetes Container Design Guidelines are a set of engineering practices and architectural conventions that govern how individual containers and container images should be built, structured, and configured so that they run predictably and efficiently under Kubernetes orchestration. These guidelines address image construction, process management inside the container, resource declaration, health signaling, configuration handling, and the way a container cooperates with the surrounding Pod and cluster control plane. They exist because Kubernetes assumes containers behave in specific ways — for example, that a container runs a single well-behaved foreground process, terminates cleanly on SIGTERM, and exposes enough observability for the kubelet and control plane to make correct scheduling, scaling, and healing decisions.
Image Construction Principles
Minimal Base Images
Container images should be built from minimal base images (distroless, Alpine, or scratch where feasible) to reduce attack surface, image size, and pull latency. Fewer installed packages means fewer CVEs to patch and a smaller footprint to scan.
Multi-Stage Builds
Build tooling, compilers, and intermediate artifacts should never ship in the final image. Multi-stage Dockerfiles separate the build environment from the runtime environment, copying only the compiled binary or runtime dependencies into the final stage.
Immutable Tags
Images referenced by Kubernetes manifests should use immutable, content-addressable tags (a git SHA or a digest) rather than mutable tags like latest. This guarantees that a given manifest always deploys the exact same bytes, which is essential for rollbacks and reproducibility.
Process and Lifecycle Behavior
Single Responsibility Per Container
Each container should run one process or one tightly coupled responsibility. Multiple unrelated services packaged into a single container defeat Kubernetes' ability to scale, restart, and monitor components independently. Related auxiliary responsibilities (log shipping, proxying, configuration reloading) belong in sidecar containers within the same Pod, not bundled into the main process.
Signal Handling
The main process must run as PID 1 or be wrapped by an init process (such as tini) that correctly forwards SIGTERM and reaps zombie processes. On termination, Kubernetes sends SIGTERM and waits for terminationGracePeriodSeconds before sending SIGKILL; containers must catch SIGTERM and shut down gracefully within that window, finishing in-flight requests and closing connections cleanly.
Statelessness
Containers should treat local filesystem writes as ephemeral. Any state that must survive a restart or reschedule belongs in a PersistentVolume, an external database, or an object store — never assumed to persist on the container's writable layer.
Resource Declaration
Requests and Limits
Every container should declare CPU and memory requests and limits. Requests inform the scheduler's bin-packing decisions; limits protect the node from a single container consuming disproportionate resources. Omitting these values leads to unpredictable scheduling and noisy-neighbor effects.
Right-Sizing
Resource values should be derived from observed usage (via profiling or historical metrics) rather than guessed. Overly generous requests waste cluster capacity; overly tight limits cause throttling or OOM kills.
Health and Observability
Liveness, Readiness, and Startup Probes
Containers should expose distinct signals for three different questions:
- A liveness probe answers whether the process is still functioning and should be restarted if not.
- A readiness probe answers whether the container is currently able to serve traffic, controlling whether it receives requests from a Service.
- A startup probe protects slow-starting applications from being killed by a liveness probe before initialization completes.
These probes must be lightweight, side-effect-free, and independent of downstream dependencies that are unrelated to the container's own health.
Structured Logging to stdout/stderr
Containers should write logs to stdout and stderr rather than to files inside the container. This allows the container runtime and cluster logging stack to collect, aggregate, and route logs without requiring the application to manage log rotation or file handles.
Configuration and Secrets
Externalized Configuration
Configuration values should be injected via environment variables or mounted ConfigMap/Secret volumes rather than baked into the image. This lets the same image be promoted across environments (development, staging, production) without rebuilding.
Secret Handling
Sensitive values (credentials, tokens, keys) must be sourced from Secret objects, never hardcoded in image layers or committed manifests. Containers should avoid writing secrets to logs or persisting them to disk unencrypted.
Security Posture
Non-Root Execution
Containers should run as a non-root user by default, enforced via securityContext.runAsNonRoot and a specific runAsUser. Root execution inside a container increases the impact of a container breakout.
Read-Only Root Filesystem
Where the application allows it, readOnlyRootFilesystem: true should be set, with explicit emptyDir mounts for any directories that genuinely require write access. This limits the ability of a compromised process to persist malicious changes.
Dropped Capabilities
Linux capabilities should be dropped to the minimum required set (drop: ["ALL"] with explicit add entries only where necessary), reducing the privileges available to a process even if it is compromised.
Example Manifest Fragment
apiVersion: apps/v1
kind: Deployment
metadata:
name: codartium-service
spec:
replicas: 3
template:
spec:
containers:
- name: codartium-service
image: registry.example.com/codartium-service@sha256:abc123...
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /healthz/ready
port: 8080
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz/live
port: 8080
periodSeconds: 10
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
Summary of Practical Effects
Following these guidelines produces containers that Kubernetes can schedule efficiently, restart safely, scale horizontally without coordination overhead, and secure with a minimal blast radius when something goes wrong. Deviating from them — bundling multiple processes, ignoring termination signals, omitting resource declarations, or running as root — tends to surface as scheduling instability, slow or failed rollouts, cascading outages during node pressure, and larger security exposure during an incident.