Back to Blog

Docker Containerization: From Basics to Production

🐳 What is Docker?

Docker is a containerization platform that packages applications and their dependencies into lightweight, portable containers. These containers can run consistently across different environments, from development to production, solving the "it works on my machine" problem.

"Docker containers wrap up software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server." - Docker Documentation

📚 Key Docker Concepts

🖼️ Docker Images

Docker images are read-only templates used to create containers. Think of them as blueprints that contain:

  • Application code and dependencies
  • Runtime environment and libraries
  • Configuration files and environment variables
  • Metadata and instructions for execution

📦 Containers

Containers are running instances of Docker images. Key characteristics:

  • Isolated: Each container runs in its own environment
  • Lightweight: Share the host OS kernel (unlike VMs)
  • Portable: Run consistently across different platforms
  • Scalable: Easy to start, stop, and replicate

📝 Dockerfile

A text file containing step-by-step instructions to build a Docker image automatically. It defines the base image, dependencies, configuration, and startup commands.

⚙️ Essential Docker Commands

🔄 Image Management

# Pull an image from Docker Hub
docker pull nginx:latest

# List local images
docker images

# Build image from Dockerfile
docker build -t my-app:v1.0 .

# Remove an image
docker rmi nginx:latest

# Tag an image
docker tag my-app:v1.0 my-app:latest

🚀 Container Operations

# Run a container (detached mode)
docker run -d -p 80:80 --name my-nginx nginx

# Run container interactively
docker run -it ubuntu:20.04 /bin/bash

# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

# Stop a container
docker stop my-nginx

# Start a stopped container
docker start my-nginx

# Remove a container
docker rm my-nginx

# View container logs
docker logs my-nginx

# Execute command in running container
docker exec -it my-nginx /bin/bash

🛠️ Creating Your First Dockerfile

👍 Best Practices Dockerfile

# Use specific version tags for reproducibility
FROM node:16-alpine

# Add metadata
LABEL maintainer="your-email@example.com"
LABEL version="1.0"
LABEL description="My Node.js application"

# Create non-root user for security
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# Set working directory
WORKDIR /app

# Copy package files first (for better caching)
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production && npm cache clean --force

# Copy application code
COPY --chown=nextjs:nodejs . .

# Switch to non-root user
USER nextjs

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# Define startup command
CMD ["npm", "start"]

📄 .dockerignore File

# Ignore unnecessary files to reduce image size
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.DS_Store
*.log

💾 Docker Volumes: Persistent Storage

Volumes provide persistent storage that survives container restarts and removals:

📊 Types of Volumes

  • Named Volumes: Managed by Docker, best for production
  • Bind Mounts: Direct host filesystem mapping
  • tmpfs Mounts: Temporary filesystem in memory
# Create a named volume
docker volume create my-data

# List volumes
docker volume ls

# Run container with named volume
docker run -v my-data:/app/data my-app

# Run container with bind mount (development)
docker run -v $(pwd):/app my-app

# Run container with read-only volume
docker run -v my-data:/app/data:ro my-app

# Inspect volume details
docker volume inspect my-data

🌐 Docker Networks: Container Communication

Networks enable secure communication between containers:

🔗 Network Types

  • Bridge: Default network for standalone containers
  • Host: Remove network isolation (use host networking)
  • Overlay: Multi-host networking for Docker Swarm
  • None: Disable networking
# Create a custom bridge network
docker network create --driver bridge my-network

# List networks
docker network ls

# Run containers on the same network
docker run -d --network my-network --name web nginx
docker run -d --network my-network --name db postgres

# Containers can communicate using container names as hostnames
# web container can reach db container at: http://db:5432

# Inspect network details
docker network inspect my-network

# Connect running container to network
docker network connect my-network existing-container

🏢 Production Best Practices

1. 🏗️ Multi-stage Builds

Reduce image size by separating build and runtime environments:

# Build stage
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm run test

# Production stage
FROM node:16-alpine AS production
WORKDIR /app

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001

# Copy only production files
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/package*.json ./

# Install only production dependencies
RUN npm ci --only=production && npm cache clean --force

USER nextjs
EXPOSE 3000
CMD ["npm", "start"]

2. 🔒 Security Best Practices

  • Use Official Images: Start with trusted base images
  • Non-root User: Never run containers as root
  • Scan Images: Use tools like docker scan or Snyk
  • Minimal Images: Use Alpine or distroless images
  • Secrets Management: Never embed secrets in images
  • Resource Limits: Set CPU and memory limits

3. 📈 Image Optimization

  • Layer Caching: Order Dockerfile instructions by change frequency
  • Alpine Linux: Use minimal base images (5MB vs 100MB+)
  • Combine RUN Commands: Reduce layers with &&
  • .dockerignore: Exclude unnecessary files
  • Clean Up: Remove package managers and caches
# Optimized Dockerfile example
FROM alpine:3.16

# Install dependencies in single layer
RUN apk add --no-cache \
    nodejs \
    npm && \
    npm install -g pm2

# Clean up in same layer
RUN apk del .build-deps && \
    rm -rf /var/cache/apk/*

🐳 Docker Compose: Multi-Container Applications

Docker Compose simplifies multi-container application management:

📝 Complete docker-compose.yml Example

version: '3.8'

services:
  # Web application
  web:
    build: 
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:password@database:5432/myapp
    depends_on:
      database:
        condition: service_healthy
    volumes:
      - ./logs:/app/logs
    networks:
      - app-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
  
  # Database
  database:
    image: postgres:14-alpine
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - db-data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - app-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5
  
  # Redis cache
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    networks:
      - app-network
    restart: unless-stopped

# Named volumes
volumes:
  db-data:
    driver: local
  redis-data:
    driver: local

# Custom network
networks:
  app-network:
    driver: bridge

🚀 Docker Compose Commands

# Start all services
docker-compose up -d

# Build and start services
docker-compose up --build

# Stop all services
docker-compose down

# Stop and remove volumes
docker-compose down -v

# View logs
docker-compose logs -f web

# Scale a service
docker-compose up --scale web=3

# Execute command in service
docker-compose exec web /bin/bash

📈 Monitoring and Debugging

Container Health Monitoring

# Monitor container resources
docker stats

# Inspect container details
docker inspect container-name

# View container processes
docker top container-name

# Monitor container events
docker events --filter container=my-app

🎯 Conclusion

Docker revolutionizes application deployment by providing consistent, portable, and scalable containerization. From development to production, Docker ensures your applications run reliably across any environment.

🚀 Next Steps in Your Docker Journey

  • Container Orchestration: Learn Kubernetes for production-scale deployments
  • CI/CD Integration: Integrate Docker into your build pipelines
  • Security Scanning: Implement vulnerability scanning in your workflow
  • Monitoring: Set up container monitoring with Prometheus and Grafana
  • Registry Management: Learn Docker Hub, ECR, or private registries