Introduction to Docker
Docker packages applications and their dependencies into lightweight, portable containers that run consistently across different environments.
Basic Docker Concepts
Dockerfile - Building Images
dockerfile
# Dockerfile for Node.js app FROM node:18-alpine # Set working directory WORKDIR /app # Copy package files COPY package*.json ./ # Install dependencies RUN npm ci --only=production # Copy application code COPY . . # Expose port EXPOSE 3000 # Create non-root user RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 # Change ownership RUN chown -R nextjs:nodejs /app USER nextjs # Start application CMD ["npm", "start"]
Basic Docker Commands
bash
# Build image docker build -t myapp:latest . # Run container docker run -d -p 3000:3000 --name myapp-container myapp:latest # List containers docker ps docker ps -a # Include stopped containers # View logs docker logs myapp-container docker logs -f myapp-container # Follow logs # Execute commands in container docker exec -it myapp-container /bin/sh # Stop and remove container docker stop myapp-container docker rm myapp-container # Remove image docker rmi myapp:latest
Multi-Stage Dockerfile
dockerfile
# Multi-stage build for React app FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM nginx:alpine AS production # Copy built files COPY --from=builder /app/dist /usr/share/nginx/html # Copy custom nginx config COPY nginx.conf /etc/nginx/nginx.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
Docker Compose Basics
Docker Compose orchestrates multi-container applications using a YAML file.
Basic docker-compose.yml
yaml
# docker-compose.yml
version: '3.8'
services:
# Web application
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
volumes:
- .:/app
- /app/node_modules
depends_on:
- db
- redis
networks:
- app-network
# PostgreSQL database
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
networks:
- app-network
# Redis cache
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- app-network
# Named volumes
volumes:
postgres_data:
redis_data:
# Custom network
networks:
app-network:
driver: bridge
Docker Compose Commands
bash
# Start all services docker-compose up docker-compose up -d # Detached mode # Build and start docker-compose up --build # Start specific service docker-compose up web # Stop services docker-compose down docker-compose down -v # Remove volumes # View logs docker-compose logs docker-compose logs web # Specific service # Execute commands docker-compose exec web npm test docker-compose exec db psql -U user -d myapp # Scale services docker-compose up -d --scale web=3
Development Environment Setup
Full-Stack Development Environment
yaml
# docker-compose.dev.yml
version: '3.8'
services:
# Frontend (React)
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- REACT_APP_API_URL=http://localhost:8000
depends_on:
- backend
# Backend (Node.js API)
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "8000:8000"
volumes:
- ./backend:/app
- /app/node_modules
environment:
- DATABASE_URL=postgresql://dev:dev@postgres:5432/devdb
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- redis
command: npm run dev
# PostgreSQL Database
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: devdb
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
volumes:
- postgres_dev_data:/var/lib/postgresql/data
- ./backend/database/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
# Redis Cache
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_dev_data:/data
# pgAdmin for database management
pgadmin:
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: admin
ports:
- "8080:80"
depends_on:
- postgres
volumes:
postgres_dev_data:
redis_dev_data:
Production-Ready Docker Setup
yaml
# docker-compose.prod.yml
version: '3.8'
services:
# Nginx reverse proxy
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- web
restart: unless-stopped
# Web application
web:
build:
context: .
dockerfile: Dockerfile.prod
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://prod_user:${DB_PASSWORD}@db:5432/proddb
depends_on:
- db
restart: unless-stopped
deploy:
replicas: 3
resources:
limits:
memory: 512M
reservations:
memory: 256M
# Production database
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: proddb
POSTGRES_USER: prod_user
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_prod_data:/var/lib/postgresql/data
restart: unless-stopped
deploy:
resources:
limits:
memory: 1G
volumes:
postgres_prod_data:
Development Dockerfiles
Frontend Development Dockerfile
dockerfile
# frontend/Dockerfile.dev FROM node:18-alpine WORKDIR /app # Install dependencies COPY package*.json ./ RUN npm install # Copy source code COPY . . # Expose port EXPOSE 3000 # Start development server CMD ["npm", "start"]
Backend Development Dockerfile
dockerfile
# backend/Dockerfile.dev FROM node:18-alpine # Install nodemon globally for hot reloading RUN npm install -g nodemon WORKDIR /app # Copy package files COPY package*.json ./ RUN npm install # Copy source code COPY . . # Expose port EXPOSE 8000 # Start with nodemon for hot reloading CMD ["nodemon", "server.js"]
Environment Configuration
.env Files
bash
# .env.development DATABASE_URL=postgresql://dev:dev@localhost:5432/devdb REDIS_URL=redis://localhost:6379 JWT_SECRET=dev-secret-key NODE_ENV=development # .env.production DATABASE_URL=postgresql://prod_user:strong_password@db:5432/proddb REDIS_URL=redis://redis:6379 JWT_SECRET=super-secure-production-key NODE_ENV=production
Environment-Specific Compose Files
bash
# Development docker-compose -f docker-compose.yml -f docker-compose.dev.yml up # Production docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d # Testing docker-compose -f docker-compose.yml -f docker-compose.test.yml up --abort-on-container-exit
Docker Best Practices
Optimized Dockerfile
dockerfile
# Use specific versions
FROM node:18.17.0-alpine
# 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 . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001 && \
chown -R nextjs:nodejs /app
USER nextjs
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Expose port
EXPOSE 3000
# Start application
CMD ["npm", "start"]
Docker Networking and Volumes
Custom Networks
yaml
# docker-compose.yml with custom networks
version: '3.8'
services:
frontend:
build: ./frontend
networks:
- frontend-network
backend:
build: ./backend
networks:
- frontend-network
- backend-network
database:
image: postgres:15
networks:
- backend-network
networks:
frontend-network:
driver: bridge
backend-network:
driver: bridge
internal: true # No external access
Volume Management
yaml
services:
web:
image: myapp
volumes:
# Named volume
- app_data:/app/data
# Bind mount (development)
- ./src:/app/src:ro # Read-only
# Anonymous volume
- /app/node_modules
volumes:
app_data:
driver: local
driver_opts:
type: none
o: bind
device: /host/path/data
Complete Example: MERN Stack
yaml
# docker-compose.mern.yml
version: '3.8'
services:
# React Frontend
client:
build:
context: ./client
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./client:/app
- /app/node_modules
environment:
- REACT_APP_API_URL=http://localhost:5000
stdin_open: true
tty: true
# Express Backend
server:
build:
context: ./server
dockerfile: Dockerfile.dev
ports:
- "5000:5000"
volumes:
- ./server:/app
- /app/node_modules
environment:
- MONGO_URI=mongodb://mongo:27017/mernapp
- JWT_SECRET=your-jwt-secret
depends_on:
- mongo
# MongoDB Database
mongo:
image: mongo:6
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
# MongoDB Admin Interface
mongo-express:
image: mongo-express
ports:
- "8081:8081"
environment:
- ME_CONFIG_MONGODB_SERVER=mongo
- ME_CONFIG_MONGODB_ADMINUSERNAME=admin
- ME_CONFIG_MONGODB_ADMINPASSWORD=password
depends_on:
- mongo
volumes:
mongo_data:
Useful Docker Commands
bash
# System management docker system df # Check disk usage docker system prune # Clean up unused resources docker system prune -a # Remove all unused images # Image management docker images # List images docker image prune # Remove unused images docker pull nginx:latest # Pull specific image # Container management docker stats # Resource usage docker inspect container # Detailed container info docker cp file container:/path # Copy files # Compose management docker-compose config # Validate compose file docker-compose ps # List compose services docker-compose pull # Pull latest images
Troubleshooting Tips
bash
# Debug container issues docker logs -f container-name docker exec -it container-name /bin/sh docker inspect container-name # Network debugging docker network ls docker network inspect network-name # Volume debugging docker volume ls docker volume inspect volume-name # Clean up everything docker-compose down -v --remove-orphans docker system prune -a --volumes