Kubernetes Best Practices

4 minute read

Throughout this tutorial, we’ve covered the building blocks of Kubernetes. This chapter consolidates the most important best practices into a single reference — patterns to follow and anti-patterns to avoid when running Kubernetes in production.

Pod and Container Best Practices

Always Set Resource Requests and Limits

# ✅ Good
resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "250m"
    memory: "256Mi"

# ❌ Bad — No resources (BestEffort QoS, first to be evicted)
containers:
- name: app
  image: my-app:1.0
  # no resources specified

Use Specific Image Tags, Never latest

# ✅ Good — Immutable, reproducible
image: nginx:1.27.3

# ❌ Bad — What version is "latest"?
image: nginx:latest

# ❌ Bad — Tag could be overwritten
image: nginx

Set Security Context

# ✅ Good — Principle of least privilege
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop:
    - ALL

Always Define Health Checks

# ✅ Good
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 20

Use topologySpreadConstraints or Pod Anti-Affinity

Spread Pods across nodes and zones for high availability:

# ✅ Good — Spread across nodes
topologySpreadConstraints:
- maxSkew: 1
  topologyKey: kubernetes.io/hostname
  whenUnsatisfiable: DoNotSchedule
  labelSelector:
    matchLabels:
      app: web

Deployment Best Practices

Use Declarative Configuration (GitOps)

# ✅ Good — All changes through YAML in Git
kubectl apply -f deployment.yaml

# ❌ Bad — Imperative changes are not tracked
kubectl scale deployment web --replicas=5
kubectl set image deployment web app=my-app:2.0

Configure Rolling Updates Properly

# ✅ Good — Zero downtime
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

# ❌ Bad — Allows Pods to go below desired count
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 50%
    maxUnavailable: 50%    # Half your Pods can be down during update

Use revisionHistoryLimit

# ✅ Good — Keep some history for rollbacks
spec:
  revisionHistoryLimit: 5

# ❌ Bad — Keeps all old ReplicaSets (default is 10)
# also bad — doesn't keep any for rollback
spec:
  revisionHistoryLimit: 0

Set terminationGracePeriodSeconds

Give your application time to shut down gracefully:

# ✅ Good
spec:
  terminationGracePeriodSeconds: 30
  containers:
  - name: app
    # Your app should handle SIGTERM and finish in-flight requests

Namespace Best Practices

Use Namespaces for Isolation

# ✅ Good — Separate concerns
kubectl create ns frontend
kubectl create ns backend
kubectl create ns monitoring
kubectl create ns data

# ❌ Bad — Everything in "default"
kubectl create deployment web ...     # default namespace
kubectl create deployment api ...     # default namespace
kubectl create deployment db ...      # default namespace

Apply ResourceQuotas and LimitRanges

# ✅ Good — Prevent resource exhaustion
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-alpha
spec:
  hard:
    pods: "30"
    requests.cpu: "10"
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"

Security Best Practices

1. Follow Least Privilege for RBAC

# ✅ Good — Specific permissions
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

# ❌ Bad — Too broad
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]

2. Use Network Policies

# ✅ Good — Default deny + explicit allow
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

3. Scan Container Images

# Scan images for vulnerabilities before deployment
trivy image my-app:1.0
# or use Snyk, Clair, Aqua, etc.

4. Use Secrets Properly

# ✅ Good
# - Enable encryption at rest
# - Use external secrets managers (Vault, AWS SM)
# - Never commit Secrets to Git

# ❌ Bad
# - Plain text credentials in ConfigMaps
# - Secrets in environment variables of Pod spec YAML committed to Git

5. Keep Images Minimal

# ✅ Good — Distroless or Alpine
FROM gcr.io/distroless/static:nonroot

# ❌ Bad — Full OS with unnecessary tools
FROM ubuntu:22.04

Configuration Best Practices

Externalize Configuration

# ✅ Good — Configuration separate from code
envFrom:
- configMapRef:
    name: app-config
- secretRef:
    name: app-secrets

# ❌ Bad — Hardcoded in the container
env:
- name: DB_HOST
  value: "10.0.1.5"       # Hardcoded IP

Use Labels Consistently

# ✅ Good — Following Kubernetes recommended labels
metadata:
  labels:
    app.kubernetes.io/name: web-app
    app.kubernetes.io/version: "1.0.0"
    app.kubernetes.io/component: frontend
    app.kubernetes.io/part-of: my-platform
    app.kubernetes.io/managed-by: helm
    environment: production
    team: platform

Production Readiness Checklist

Infrastructure

  • Multi-node cluster (at least 3 control plane nodes for HA)
  • Nodes across multiple availability zones
  • etcd backup strategy in place
  • Cluster autoscaler configured
  • Container runtime up to date

Workloads

  • Resource requests and limits on all containers
  • Liveness and readiness probes configured
  • Pod disruption budgets set
  • Anti-affinity rules for critical workloads
  • Graceful shutdown handling (SIGTERM)

Security

  • RBAC configured (no cluster-admin for regular users)
  • Network Policies applied (default-deny)
  • Pod Security Standards enforced
  • Secrets encrypted at rest
  • Container images scanned for vulnerabilities
  • Non-root containers

Observability

  • Metrics collection (Prometheus)
  • Dashboards (Grafana)
  • Centralized logging (EFK/Loki)
  • Alerting configured (Alertmanager)
  • Distributed tracing (for microservices)

Operations

  • Declarative configuration in Git (GitOps)
  • CI/CD pipeline for deployments
  • Rollback procedure tested
  • Disaster recovery tested
  • Resource quotas per namespace

Pod Disruption Budgets (PDB)

Ensure minimum availability during voluntary disruptions (node drain, cluster upgrade):

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  minAvailable: 2          # At least 2 Pods must always be running
  # or: maxUnavailable: 1  # At most 1 Pod can be down
  selector:
    matchLabels:
      app: web

Summary of Key Rules

Area Rule
Images Use specific tags, scan for vulnerabilities, keep minimal
Resources Always set requests and limits
Health Always configure readiness and liveness probes
Security Run as non-root, drop capabilities, use Network Policies
RBAC Least privilege, no cluster-admin for regular use
Config Externalize with ConfigMaps and Secrets
Labels Use consistent, descriptive labels
Deployments Use rolling updates with maxUnavailable: 0
Namespaces Separate concerns with quotas and limits
Observability Prometheus + Grafana + centralized logging
GitOps All configuration in Git, applied via CI/CD

What’s Next?

Congratulations! You’ve completed the Kubernetes Foundation Tutorial. You now have a solid understanding of all the core concepts. Here’s where to go next.

Next: What’s Next? →

Updated: