Kubernetes Best Practices
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.