Production-Readiness Checklist

A hardened securityContext, a default-deny NetworkPolicy, and the boxes every workload should tick before it carries real traffic.

Before a workload takes production traffic, run it through a checklist. Two items — a locked-down securityContext and a default-deny NetworkPolicy — deliver the most safety for the least effort, so start there.

Harden the securityContext

Containers run as root by default, which turns a container escape into a node compromise. A hardened securityContext closes that door:

  • runAsNonRoot: true and a specific runAsUser — never run as root.
  • allowPrivilegeEscalation: false — block setuid escalation.
  • readOnlyRootFilesystem: true — force writes to explicit volumes only.
  • capabilities: drop: ["ALL"] — strip Linux capabilities, add back only what you truly need.
  • seccompProfile: RuntimeDefault — enable syscall filtering.

Default-deny network

By default any Pod can talk to any other Pod. A NetworkPolicy that selects everything and allows nothing flips the cluster to deny-by-default; you then add narrow allow rules per service. (Your CNI must enforce policies — Calico or Cilium do; plain flannel does not.)

The full checklist

  • Resource requests and memory limits set (QoS understood).
  • Liveness, readiness, and startup probes wired correctly.
  • PodDisruptionBudget for every multi-replica Deployment.
  • Dedicated ServiceAccount with least-privilege RBAC; token automount off if unused.
  • Secrets from an external store, mounted as files, etcd encrypted at rest.
  • Topology spread across zones; anti-affinity for critical apps.
  • Hardened securityContext + default-deny NetworkPolicy.
  • Pinned image digests (not :latest), imagePullPolicy understood.

Example

Example · yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hardened-web
spec:
  replicas: 3
  selector:
    matchLabels: { app: hardened-web }
  template:
    metadata:
      labels: { app: hardened-web }
    spec:
      automountServiceAccountToken: false   # app never calls the API
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: web
          image: nginx@sha256:...pin-a-digest-here...   # never :latest
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: prod
spec:
  podSelector: {}            # selects every Pod in the namespace
  policyTypes: ["Ingress", "Egress"]   # deny all until allow rules are added

When to use it

  • An engineering team runs a pre-production checklist that verifies every Deployment has resource limits, probes, and a PodDisruptionBudget before promoting to production.
  • A security team validates the checklist item that no container runs as root and all images are from the approved private registry before approving a release.
  • A platform team uses Polaris or kube-score to automatically scan all manifests in CI against the production checklist and fail the pipeline on critical violations.

More examples

Scan manifests with kube-score

Runs kube-score against a deployment manifest to surface security and reliability issues mapped against Kubernetes best practices.

Example · bash
# Install kube-score
brew install kube-score

# Score a deployment manifest
kube-score score deployment.yaml
# CRITICAL  Container Security Context  runAsNonRoot is not set
# WARNING   Pod Disruption Budget       No matching PDB found
# OK        Resource Limits             All limits are set

Full production Deployment checklist spec

A production-grade pod spec that satisfies the full checklist: pinned image, resource limits, probes, non-root user, and read-only filesystem.

Example · yaml
spec:
  replicas: 3
  strategy:
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    spec:
      serviceAccountName: app-sa  # Dedicated SA
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
      containers:
        - name: app
          image: myapp:v1.4.2  # Pinned tag
          resources:
            requests: { cpu: 100m, memory: 128Mi }
            limits:   { cpu: 500m, memory: 256Mi }
          readinessProbe:
            httpGet: { path: /ready, port: 8080 }
          livenessProbe:
            httpGet: { path: /healthz, port: 8080 }
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true

Production readiness gate in CI

A CI script that enforces four production gates in sequence: server-side dry-run validation, kube-score review, security scan, and pinned image tag check.

Example · bash
#!/bin/bash
set -e

# 1. Lint manifests
kubectl apply --dry-run=server -f manifests/

# 2. Score for best practices
kube-score score manifests/ --exit-one-on-warning

# 3. Static security scan
kubesec scan manifests/deployment.yaml

# 4. Check image is not :latest
grep ':latest' manifests/deployment.yaml && echo 'ERROR: Use pinned tag' && exit 1

echo 'All production gates passed'

Discussion

  • Be the first to comment on this lesson.