Docker¶
Container platform that packages applications with their dependencies into isolated, portable units. Lighter than VMs, faster startup, consistent environments from dev to prod. Industry standard for containerization. Kubernetes runs Docker containers (well, technically any OCI-compliant runtime).
2026 Update
Docker Desktop is mature and feature-rich. BuildKit is default (faster builds, better caching). Docker Compose v2 is rewritten in Go (much faster). Multi-platform builds are standard. Kubernetes dropped dockershim but Docker containers still work (containerd).
Quick Hits¶
# Container lifecycle
docker run -it ubuntu bash # Run interactive container
docker run -d -p 80:80 nginx # Run detached with port mapping
docker ps # List running containers
docker ps -a # List all containers (including stopped)
docker stop container_id # Graceful stop (SIGTERM)
docker kill container_id # Force stop (SIGKILL)
docker rm container_id # Remove stopped container
docker logs -f container_id # Follow container logs
# Image management
docker images # List local images
docker pull nginx:latest # Download image
docker build -t myapp:1.0 . # Build from Dockerfile
docker tag myapp:1.0 user/myapp:1.0 # Tag image
docker push user/myapp:1.0 # Push to registry
docker rmi image_id # Remove image
docker image prune # Remove unused images
# Inspection and debugging
docker exec -it container_id bash # Execute command in running container
docker inspect container_id # JSON details about container
docker logs container_id # View container logs
docker stats # Real-time resource usage
docker top container_id # Running processes in container
# Cleanup
docker system prune # Remove unused data (images, containers, networks)
docker system prune -a # Remove ALL unused images (not just dangling)
docker volume prune # Remove unused volumes (careful!)
# Docker Compose (multi-container apps)
docker compose up -d # Start services in background
docker compose down # Stop and remove containers
docker compose logs -f # Follow logs from all services
docker compose ps # List running services
docker compose restart service_name # Restart specific service
Real talk:
- Use
docker compose(v2), notdocker-compose(v1 deprecated) - Always name your containers (
--name) for easier management - Map volumes for persistent data (
-v host:container) - Don't run containers as root - use
USERdirective in Dockerfile docker system prune -ais your friend when disk fills up
# Dockerfile best practices (multi-stage build)
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production # (1)!
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:20-alpine
WORKDIR /app
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 # (2)!
# Copy artifacts from builder
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./
# Switch to non-root user
USER nodejs # (3)!
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js || exit 1 # (4)!
EXPOSE 3000
CMD ["node", "dist/server.js"]
npm ciis faster and more reliable thannpm installfor CI/CD- Never run containers as root - creates user with UID 1001
- Switches to non-root user - security best practice
- Health checks allow Docker/K8s to detect unhealthy containers
# docker-compose.yml (realistic production-ish setup)
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
NODE_ENV: production
DATABASE_URL: postgres://postgres:password@db:5432/myapp # (1)!
depends_on:
db:
condition: service_healthy # (2)!
restart: unless-stopped # (3)!
volumes:
- ./logs:/app/logs # Persist logs
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: password # (4)!
volumes:
- postgres-data:/var/lib/postgresql/data # (5)!
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --appendonly yes # (6)!
volumes:
- redis-data:/data
networks:
- app-network
volumes:
postgres-data: # Named volume (persists data)
redis-data:
networks:
app-network:
driver: bridge
- Service names resolve to container IPs automatically
- Wait for db health check before starting web service
- Auto-restart unless explicitly stopped
- Use
.envfile or secrets in production, not hardcoded passwords - Named volumes persist data across container recreations
- Enable AOF persistence for Redis
Why this works:
- Multi-stage builds reduce image size (only production dependencies)
- Non-root user improves security posture
- Health checks enable automatic recovery
- Named volumes prevent data loss
- Service dependencies ensure startup order
- Networks isolate containers from host
Build Optimization
- Layer caching - Order Dockerfile commands by change frequency (COPY dependencies before code)
- BuildKit - Enable with
DOCKER_BUILDKIT=1for parallel builds and better caching - Multi-stage builds - Separate build and runtime stages, 10x smaller images
- .dockerignore - Exclude node_modules, .git, tests from build context (faster builds)
- Alpine images - Smaller base images (5MB vs 100MB+), but glibc issues possible
- Specific tags - Use
node:20.11.0-alpinenotnode:latest(reproducible builds)
Security Best Practices
- Never run as root - Use
USERdirective in Dockerfile - Scan images -
docker scan image:tagor use Trivy, Snyk - Minimal base images -
scratch,alpine, or distroless - No secrets in images - Use env vars, Docker secrets, or external secret managers
- Read-only filesystem -
docker run --read-onlywhen possible - Drop capabilities -
--cap-drop=ALL --cap-add=NET_BIND_SERVICE - Resource limits - Set memory/CPU limits to prevent container takeover
Development Workflow
- Bind mounts - Live code reload in dev (
-v $(pwd):/app) - Docker Compose - Define entire stack (app + db + redis)
- Override files - Use
docker-compose.override.ymlfor local settings - BuildKit cache mounts -
RUN --mount=type=cache,target=/root/.npmfor faster builds - Docker Desktop - Kubernetes built-in (single-node cluster for testing)
- Dev containers - VSCode Remote Containers for consistent dev environments
Common Gotchas
- Disk space - Docker images fill disk fast, run
docker system pruneregularly - Port conflicts - Check if port already in use (
lsof -i :8080on macOS/Linux) - Volume permissions - Host user != container user (use
chownor named volumes) - Networking - Container-to-container use service names, not localhost
- Logs fill disk - Configure log rotation (
--log-opt max-size=10m) - Layer caching gotcha -
COPY . .invalidates all subsequent layers - Platform mismatch - M1/M2 Macs need
--platform linux/amd64for production images
Production Considerations
- Orchestration - Use Kubernetes, Docker Swarm, or ECS (don't run containers manually)
- Monitoring - Prometheus + Grafana for metrics, ELK for logs
- Registry - Private registry (ECR, Docker Hub private, Harbor, Artifactory)
- Image signing - Docker Content Trust (DCT) or Cosign for supply chain security
- Resource limits - Always set memory/CPU limits in production
- Health checks - Required for orchestrators to detect failures
- Graceful shutdown - Handle SIGTERM properly (Node.js often doesn't by default)
Learning Resources¶
Free Resources¶
- Docker Documentation - Official docs, comprehensive and well-written
- Docker Getting Started - Official tutorial, hands-on
- Play with Docker - Browser-based Docker playground (no install needed)
- Docker Curriculum - Beginner-friendly tutorial
- Awesome Docker - Curated list of tools and resources
Practice Projects¶
Beginner
- Static website - Nginx serving HTML/CSS/JS
- Node.js API - Express app with multi-stage build
- Python Flask app - Web app with Redis for sessions
Intermediate
- Full-stack app - React frontend + Node backend + PostgreSQL (docker-compose)
- Microservices - Multiple services communicating via REST/gRPC
- CI/CD pipeline - GitHub Actions building and pushing images
Advanced
- Multi-arch builds - ARM64 + AMD64 images with buildx
- Private registry - Self-hosted Harbor with scanning
- Security hardening - Distroless images, rootless Docker, Falco monitoring
Worth Checking¶
Last Updated: 2026-02-02 | Vibe Check: Essential - Docker is the standard for containerization. If you're deploying modern applications, you're using Docker (or at least OCI containers). Learn it well. Tags: docker, containers, devops, virtualization