All code helpers
DevOpsYAML

Kubernetes Deployment with probes and zero-downtime rollouts

A production-ready baseline for a stateless HTTP service: readiness, liveness and startup probes, resource limits, a non-root security context and rolling updates that never drop capacity.

#kubernetes#deployment#probes#k8s
k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  labels: { app: api }
spec:
  replicas: 3
  selector:
    matchLabels: { app: api }
  strategy:
    type: RollingUpdate
    rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
  template:
    metadata:
      labels: { app: api }
    spec:
      terminationGracePeriodSeconds: 30
      securityContext: { runAsNonRoot: true, runAsUser: 1001 }
      containers:
        - name: api
          image: ghcr.io/acme/api:sha-1a2b3c4
          ports: [{ name: http, containerPort: 3000 }]
          envFrom: [{ secretRef: { name: api-env } }]
          resources:
            requests: { cpu: 250m, memory: 256Mi }
            limits: { memory: 512Mi }
          startupProbe: # gives slow boots time before liveness kicks in
            httpGet: { path: /healthz, port: http }
            periodSeconds: 5
            failureThreshold: 24
          readinessProbe: # controls whether the pod receives traffic
            httpGet: { path: /readyz, port: http }
            periodSeconds: 5
            failureThreshold: 2
          livenessProbe: # restarts the container if the process is stuck
            httpGet: { path: /healthz, port: http }
            periodSeconds: 10
            failureThreshold: 3
          lifecycle:
            preStop: # let endpoints update before SIGTERM arrives
              sleep: { seconds: 5 }
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector: { app: api }
  ports: [{ name: http, port: 80, targetPort: http }]

How it works & gotchas

  • 01Readiness decides traffic, liveness decides restarts. Liveness should only check that the process responds, never the database, or one DB blip restarts every pod at once.
  • 02maxUnavailable: 0 with maxSurge: 1 keeps full capacity during a rollout: a new pod must be ready before an old one is removed.
  • 03Only a memory limit is set. CPU limits cause throttling and latency spikes; a CPU request is enough for scheduling.
  • 04The native preStop.sleep action needs Kubernetes 1.30+. On older clusters use exec: { command: ["sleep", "5"] }, which requires a shell in the image.
  • 05readOnlyRootFilesystem breaks apps that write to disk. Mount an emptyDir at /tmp if yours does.

Need this wired into a real system?

We build and harden production software end to end.

Talk to an engineer