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: trueand a specificrunAsUser— 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),imagePullPolicyunderstood.
Example
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 addedWhen 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.
# 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 setFull 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.
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: trueProduction 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.
#!/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