Kubernetes Manifest Rules
These are the enforceable rules for writing production Kubernetes manifests - the workload spec fields that keep a service healthy, scheduled correctly, and hardened.
The through-line: every workload declares its resource footprint, its health, its identity, and its security posture. Nothing is left implicit for the scheduler or an attacker to decide.
How to use this list
Validate structure with kubeconform -strict, then enforce the security-critical items with an admission policy (Kyverno or Gatekeeper) and Pod Security Standards.
Each item is a gate. A manifest that cannot pass it should fail CI or be rejected at admission.
Resources and scheduling
- Set CPU and memory requests - the scheduler uses requests to place pods; missing requests cause overcommit and evictions.
- Set a memory limit - prevents a leaking container from OOM-killing its neighbors on the node.
- Be deliberate about CPU limits - CPU limits throttle rather than kill; often set requests and skip a hard CPU limit for latency-sensitive services.
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
memory: "256Mi"- Run
replicas >= 2- a single replica has no availability during a node drain or crash. - Add a PodDisruptionBudget - protect availability during voluntary disruptions like upgrades.
- Spread across zones/nodes - use
topologySpreadConstraintsso one node or zone loss does not take the service down.
Health probes
- Define a readiness probe - it gates traffic; without it, a pod receives requests before it is ready.
- Define a liveness probe - it restarts a wedged process, but keep it cheap and tolerant to avoid restart storms.
- Add a startup probe for slow starters - it holds off liveness until the app has booted.
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /livez
port: 8080
periodSeconds: 15
failureThreshold: 3Labels, images, and rollout
- Apply the recommended labels -
app.kubernetes.io/name,instance,version,part-of, andmanaged-byfor selection and observability. - Pin the image by digest -
image: registry/api@sha256:...; never:latestin a cluster. - Set an explicit
imagePullPolicy-IfNotPresentwith digest pinning; avoidAlwayssurprises. - Configure RollingUpdate strategy - tune
maxUnavailableandmaxSurgeso rollouts respect capacity. - Rely on readiness during rollout - a bad new revision should never receive traffic before it is ready.
Security context and Pod Security Standards
- Enforce PSS
restrictedon the namespace - labelpod-security.kubernetes.io/enforce: restricted.
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest- Set
runAsNonRoot: trueand a numericrunAsUser- refuse to start as UID 0. - Set
allowPrivilegeEscalation: false- block setuid escalation inside the container. - Drop all capabilities -
capabilities.drop: ["ALL"], add back only what is proven necessary. - Use
readOnlyRootFilesystem: true- mount anemptyDirfor any writable path the app needs. - Set
seccompProfile.type: RuntimeDefault- required by the restricted profile.
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefaultIdentity, networking, and config
- Use a dedicated ServiceAccount - and set
automountServiceAccountToken: falsewhen the pod does not call the API. - Scope RBAC to least privilege - Roles over ClusterRoles, no wildcard verbs or resources.
- Apply a default-deny NetworkPolicy - then allow only required ingress and egress per workload.
- Mount config and secrets, do not inline them - use ConfigMaps and a secrets manager via CSI or External Secrets.
- Set
terminationGracePeriodSecondsand handle SIGTERM - so pods drain connections cleanly on rollout.
Remember the runtime boundary: these manifests are executed by containerd through the CRI on each node. Docker is not the in-cluster runtime, so nothing here depends on a Docker daemon running on the node.
FAQs
Should I always set a CPU limit? Not always. CPU limits throttle and can hurt tail latency; set requests for scheduling and reserve hard CPU limits for tenancy isolation cases.
Why is a memory limit safer than a CPU limit? Memory is incompressible - exceeding it triggers an OOM kill. A limit contains a leak to one pod instead of taking down the node.
Do I need all three probe types? Readiness is essential. Add liveness carefully, and a startup probe only when initialization is slow enough to trip liveness.
What does PSS restricted actually block?
Privilege escalation, running as root, host namespaces, most capabilities, and writable root filesystems - the common container escape vectors.
Is a default-deny NetworkPolicy disruptive to roll out? Roll it out per namespace after mapping required flows; start in a non-critical namespace and add allow rules from observed traffic.
How do I pin images and still get patches? Pin by digest for reproducibility and use an automation (Argo CD Image Updater or a bot) to propose digest bumps through Git.
Related
- Containers Rules Checklist
- Dockerfile Rules
- Why Rules Beat Tribal Knowledge
- ADR Template
- Containers Rules Best Practices
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).