✦ For everyone, free.

Practical knowledge for real and everyday life

Home

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.