DevOps

Docker & Kubernetes in Production: A Complete DevOps Guide

A
Admin
March 9, 2026 • 5 min read • 871 words
8
Overall Scoreout of 10

Rating

8/10

Verdict

Docker is essential for every team regardless of scale. Kubernetes pays off at 5+ services or when you need auto-scaling — for smaller setups, consider Docker Compose or a managed PaaS.

Pros

  • Consistent environments from dev to prod
  • Horizontal scaling is trivial with K8s
  • Rolling updates with zero downtime
  • Excellent ecosystem (Helm, ArgoCD, etc.)
  • Strong community and documentation

Cons

  • Kubernetes has a steep learning curve
  • Operational overhead for small teams
  • Debugging containers can be tricky
  • Resource overhead vs bare metal

Docker & Kubernetes in Production: A Complete DevOps Guide

Containerization has fundamentally changed how we build, ship, and run software. Docker provides the packaging standard while Kubernetes provides the orchestration platform that makes running containers at scale manageable. This guide takes you from writing your first Dockerfile all the way to production-grade Kubernetes deployments with auto-scaling, rolling updates, and observability.

Docker Fundamentals

Docker packages an application and its dependencies into a container — an isolated, reproducible environment that runs identically everywhere. The core components are:

  • Dockerfile — Instructions to build a Docker image
  • Image — A read-only template created from a Dockerfile
  • Container — A running instance of an image
  • Registry — Storage for images (Docker Hub, ECR, GCR)

Writing Optimized Dockerfiles

A production Dockerfile should be small, fast to build, and secure. Use multi-stage builds to separate build dependencies from the final image:

# Stage 1: Dependencies FROM node:20-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --only=production # Stage 2: Builder FROM node:20-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build # Stage 3: Production runner (smallest possible image) FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=deps /app/node_modules ./node_modules USER nextjs EXPOSE 3000 ENV PORT=3000 CMD ["node", "server.js"]

Key optimization rules:

  • Use Alpine-based images (node:20-alpine) — 5x smaller than full Debian
  • Copy only what's needed in the final stage
  • Run as a non-root user for security
  • Order layers from least to most frequently changed
  • Use .dockerignore to exclude node_modules, .git, .env files

# .dockerignore node_modules .next .git .env* *.log coverage

Docker Compose for Local Development

# docker-compose.yml version: '3.9' services: app: build: context: . dockerfile: Dockerfile target: builder ports: - "3000:3000" environment: - DATABASE_URL=postgresql://postgres:password@db:5432/myapp - REDIS_URL=redis://cache:6379 volumes: - .:/app - /app/node_modules depends_on: db: condition: service_healthy cache: condition: service_started db: image: postgres:16-alpine environment: POSTGRES_DB: myapp POSTGRES_USER: postgres POSTGRES_PASSWORD: password volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine volumes: - redis_data:/data volumes: postgres_data: redis_data:

Kubernetes Architecture

Kubernetes (K8s) is a container orchestration platform that automates deployment, scaling, and management of containerized applications. Key concepts:

  • Node — A worker machine (VM or physical) that runs pods
  • Pod — The smallest deployable unit, containing one or more containers
  • Deployment — Manages a set of identical pods with rolling updates
  • Service — Exposes pods as a stable network endpoint
  • Ingress — Manages external HTTP/HTTPS access to services
  • ConfigMap/Secret — Configuration and sensitive data injection

Writing Kubernetes Manifests

# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-app namespace: production labels: app: my-app spec: replicas: 3 selector: matchLabels: app: my-app strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 # Zero-downtime deploys template: metadata: labels: app: my-app spec: containers: - name: my-app image: myregistry/my-app:v1.2.3 ports: - containerPort: 3000 env: - name: NODE_ENV value: production - name: DATABASE_URL valueFrom: secretKeyRef: name: app-secrets key: database-url resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" readinessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 15 periodSeconds: 20 affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: my-app topologyKey: kubernetes.io/hostname

# service.yaml apiVersion: v1 kind: Service metadata: name: my-app-service namespace: production spec: selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 3000 type: ClusterIP

Ingress with TLS

# ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress namespace: production annotations: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: ingressClassName: nginx tls: - hosts: - app.example.com secretName: my-app-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-app-service port: number: 80

Horizontal Pod Autoscaler

# hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: my-app-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: my-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80

CI/CD Pipeline with GitHub Actions

# .github/workflows/deploy.yml name: Build and Deploy on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Login to Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and Push Image uses: docker/build-push-action@v5 with: context: . push: true tags: | ghcr.io/${{ github.repository }}:latest ghcr.io/${{ github.repository }}:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max - name: Deploy to Kubernetes uses: azure/k8s-deploy@v4 with: manifests: k8s/ images: ghcr.io/${{ github.repository }}:${{ github.sha }}

Observability: Logs, Metrics & Traces

Production K8s requires comprehensive observability. The standard stack is:

  • Metrics: Prometheus + Grafana (kube-prometheus-stack Helm chart)
  • Logs: Loki + Grafana or ELK Stack
  • Traces: OpenTelemetry + Jaeger or Tempo

# Install kube-prometheus-stack helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install monitoring prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --create-namespace \ --set grafana.adminPassword=securepassword

Conclusion

Docker and Kubernetes form the foundation of modern cloud-native infrastructure. Docker gives you portable, reproducible builds. Kubernetes gives you the automation to run them reliably at scale. Start with Docker Compose for local dev, containerize your builds with multi-stage Dockerfiles, and graduate to Kubernetes when you need auto-scaling, rolling deployments, or multi-region resilience.

#Docker#Kubernetes#DevOps#Cloud#Infrastructure
Docker & Kubernetes in Production: A Complete DevOps Guide | Pulse