$ -weight: 500;">docker --version
# Docker version 26.x.x -weight: 500;">docker compose version
# Docker Compose version v2.x.x
-weight: 500;">docker --version
# Docker version 26.x.x -weight: 500;">docker compose version
# Docker Compose version v2.x.x
-weight: 500;">docker --version
# Docker version 26.x.x -weight: 500;">docker compose version
# Docker Compose version v2.x.x
# 1. Base image โ what you're building ON TOP OF
FROM node:20-alpine # 2. Set the working directory inside the container
WORKDIR /app # 3. Copy dependency files first (for layer caching)
COPY package*.json ./ # 4. Install dependencies
RUN -weight: 500;">npm -weight: 500;">install # 5. Copy the rest of your source code
COPY . . # 6. Expose the port your app listens on
EXPOSE 3000 # 7. The command to run when the container starts
CMD ["node", "server.js"]
# 1. Base image โ what you're building ON TOP OF
FROM node:20-alpine # 2. Set the working directory inside the container
WORKDIR /app # 3. Copy dependency files first (for layer caching)
COPY package*.json ./ # 4. Install dependencies
RUN -weight: 500;">npm -weight: 500;">install # 5. Copy the rest of your source code
COPY . . # 6. Expose the port your app listens on
EXPOSE 3000 # 7. The command to run when the container starts
CMD ["node", "server.js"]
# 1. Base image โ what you're building ON TOP OF
FROM node:20-alpine # 2. Set the working directory inside the container
WORKDIR /app # 3. Copy dependency files first (for layer caching)
COPY package*.json ./ # 4. Install dependencies
RUN -weight: 500;">npm -weight: 500;">install # 5. Copy the rest of your source code
COPY . . # 6. Expose the port your app listens on
EXPOSE 3000 # 7. The command to run when the container starts
CMD ["node", "server.js"]
my-app/
โโโ src/
โ โโโ index.js
โโโ package.json
โโโ package-lock.json
โโโ Dockerfile
my-app/
โโโ src/
โ โโโ index.js
โโโ package.json
โโโ package-lock.json
โโโ Dockerfile
my-app/
โโโ src/
โ โโโ index.js
โโโ package.json
โโโ package-lock.json
โโโ Dockerfile
FROM node:20-alpine WORKDIR /app # Copy lockfile and package.json first for cache efficiency
COPY package*.json ./
RUN -weight: 500;">npm ci --only=production COPY src/ ./src/ EXPOSE 3000
CMD ["node", "src/index.js"]
FROM node:20-alpine WORKDIR /app # Copy lockfile and package.json first for cache efficiency
COPY package*.json ./
RUN -weight: 500;">npm ci --only=production COPY src/ ./src/ EXPOSE 3000
CMD ["node", "src/index.js"]
FROM node:20-alpine WORKDIR /app # Copy lockfile and package.json first for cache efficiency
COPY package*.json ./
RUN -weight: 500;">npm ci --only=production COPY src/ ./src/ EXPOSE 3000
CMD ["node", "src/index.js"]
# Build the image and tag it
-weight: 500;">docker build -t my-node-app . # Run it, mapping host port 8080 โ container port 3000
-weight: 500;">docker run -p 8080:3000 my-node-app
# Build the image and tag it
-weight: 500;">docker build -t my-node-app . # Run it, mapping host port 8080 โ container port 3000
-weight: 500;">docker run -p 8080:3000 my-node-app
# Build the image and tag it
-weight: 500;">docker build -t my-node-app . # Run it, mapping host port 8080 โ container port 3000
-weight: 500;">docker run -p 8080:3000 my-node-app
FROM python:3.12-slim WORKDIR /app # Install dependencies
COPY requirements.txt .
RUN -weight: 500;">pip -weight: 500;">install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
FROM python:3.12-slim WORKDIR /app # Install dependencies
COPY requirements.txt .
RUN -weight: 500;">pip -weight: 500;">install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
FROM python:3.12-slim WORKDIR /app # Install dependencies
COPY requirements.txt .
RUN -weight: 500;">pip -weight: 500;">install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# -weight: 500;">docker-compose.yml
services: app: build: . ports: - "3000:3000" environment: - DATABASE_URL=postgres://user:password@db:5432/mydb - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started volumes: - .:/app # Mount source code for hot reload - /app/node_modules # Prevent host node_modules from overwriting db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydb volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d mydb"] interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine ports: - "6379:6379" volumes: postgres_data:
# -weight: 500;">docker-compose.yml
services: app: build: . ports: - "3000:3000" environment: - DATABASE_URL=postgres://user:password@db:5432/mydb - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started volumes: - .:/app # Mount source code for hot reload - /app/node_modules # Prevent host node_modules from overwriting db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydb volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d mydb"] interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine ports: - "6379:6379" volumes: postgres_data:
# -weight: 500;">docker-compose.yml
services: app: build: . ports: - "3000:3000" environment: - DATABASE_URL=postgres://user:password@db:5432/mydb - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started volumes: - .:/app # Mount source code for hot reload - /app/node_modules # Prevent host node_modules from overwriting db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydb volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d mydb"] interval: 5s timeout: 5s retries: 5 cache: image: redis:7-alpine ports: - "6379:6379" volumes: postgres_data:
# Start all services in the background
-weight: 500;">docker compose up -d # View logs
-weight: 500;">docker compose logs -f app # Stop everything
-weight: 500;">docker compose down # Stop and -weight: 500;">remove volumes (wipes database data)
-weight: 500;">docker compose down -v
# Start all services in the background
-weight: 500;">docker compose up -d # View logs
-weight: 500;">docker compose logs -f app # Stop everything
-weight: 500;">docker compose down # Stop and -weight: 500;">remove volumes (wipes database data)
-weight: 500;">docker compose down -v
# Start all services in the background
-weight: 500;">docker compose up -d # View logs
-weight: 500;">docker compose logs -f app # Stop everything
-weight: 500;">docker compose down # Stop and -weight: 500;">remove volumes (wipes database data)
-weight: 500;">docker compose down -v
# .env (add this to .gitignore!)
POSTGRES_PASSWORD=supersecret
API_KEY=abc123
# .env (add this to .gitignore!)
POSTGRES_PASSWORD=supersecret
API_KEY=abc123
# .env (add this to .gitignore!)
POSTGRES_PASSWORD=supersecret
API_KEY=abc123
services: app: environment: - API_KEY=${API_KEY} db: environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
services: app: environment: - API_KEY=${API_KEY} db: environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
services: app: environment: - API_KEY=${API_KEY} db: environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
my-app/
โโโ -weight: 500;">docker-compose.yml # Base config
โโโ -weight: 500;">docker-compose.dev.yml # Dev overrides (hot reload, debug ports)
โโโ -weight: 500;">docker-compose.prod.yml # Prod overrides (replicas, logging)
my-app/
โโโ -weight: 500;">docker-compose.yml # Base config
โโโ -weight: 500;">docker-compose.dev.yml # Dev overrides (hot reload, debug ports)
โโโ -weight: 500;">docker-compose.prod.yml # Prod overrides (replicas, logging)
my-app/
โโโ -weight: 500;">docker-compose.yml # Base config
โโโ -weight: 500;">docker-compose.dev.yml # Dev overrides (hot reload, debug ports)
โโโ -weight: 500;">docker-compose.prod.yml # Prod overrides (replicas, logging)
services: app: volumes: - .:/app command: -weight: 500;">npm run dev environment: - NODE_ENV=development
services: app: volumes: - .:/app command: -weight: 500;">npm run dev environment: - NODE_ENV=development
services: app: volumes: - .:/app command: -weight: 500;">npm run dev environment: - NODE_ENV=development
services: app: -weight: 500;">restart: always environment: - NODE_ENV=production deploy: replicas: 2
services: app: -weight: 500;">restart: always environment: - NODE_ENV=production deploy: replicas: 2
services: app: -weight: 500;">restart: always environment: - NODE_ENV=production deploy: replicas: 2
# Development
-weight: 500;">docker compose -f -weight: 500;">docker-compose.yml -f -weight: 500;">docker-compose.dev.yml up # Production
-weight: 500;">docker compose -f -weight: 500;">docker-compose.yml -f -weight: 500;">docker-compose.prod.yml up -d
# Development
-weight: 500;">docker compose -f -weight: 500;">docker-compose.yml -f -weight: 500;">docker-compose.dev.yml up # Production
-weight: 500;">docker compose -f -weight: 500;">docker-compose.yml -f -weight: 500;">docker-compose.prod.yml up -d
# Development
-weight: 500;">docker compose -f -weight: 500;">docker-compose.yml -f -weight: 500;">docker-compose.dev.yml up # Production
-weight: 500;">docker compose -f -weight: 500;">docker-compose.yml -f -weight: 500;">docker-compose.prod.yml up -d
-weight: 500;">docker images # List all local images
-weight: 500;">docker pull nginx:alpine # Pull image from Docker Hub
-weight: 500;">docker rmi my-app # Remove an image
-weight: 500;">docker image prune # Remove unused images
-weight: 500;">docker images # List all local images
-weight: 500;">docker pull nginx:alpine # Pull image from Docker Hub
-weight: 500;">docker rmi my-app # Remove an image
-weight: 500;">docker image prune # Remove unused images
-weight: 500;">docker images # List all local images
-weight: 500;">docker pull nginx:alpine # Pull image from Docker Hub
-weight: 500;">docker rmi my-app # Remove an image
-weight: 500;">docker image prune # Remove unused images
-weight: 500;">docker ps # List running containers
-weight: 500;">docker ps -a # List all containers (including stopped)
-weight: 500;">docker -weight: 500;">stop <container_id> # Gracefully -weight: 500;">stop a container
-weight: 500;">docker rm <container_id> # Remove a stopped container
-weight: 500;">docker logs -f <container_id> # Tail logs from a container
-weight: 500;">docker exec -it <id> sh # Open a shell inside a running container
-weight: 500;">docker ps # List running containers
-weight: 500;">docker ps -a # List all containers (including stopped)
-weight: 500;">docker -weight: 500;">stop <container_id> # Gracefully -weight: 500;">stop a container
-weight: 500;">docker rm <container_id> # Remove a stopped container
-weight: 500;">docker logs -f <container_id> # Tail logs from a container
-weight: 500;">docker exec -it <id> sh # Open a shell inside a running container
-weight: 500;">docker ps # List running containers
-weight: 500;">docker ps -a # List all containers (including stopped)
-weight: 500;">docker -weight: 500;">stop <container_id> # Gracefully -weight: 500;">stop a container
-weight: 500;">docker rm <container_id> # Remove a stopped container
-weight: 500;">docker logs -f <container_id> # Tail logs from a container
-weight: 500;">docker exec -it <id> sh # Open a shell inside a running container
# Open an interactive shell in a running container
-weight: 500;">docker exec -it my-app-container sh # Run a one-off command
-weight: 500;">docker run --rm -it node:20-alpine node --version # Inspect a container's config, network, volumes
-weight: 500;">docker inspect <container_id> # Check resource usage
-weight: 500;">docker stats
# Open an interactive shell in a running container
-weight: 500;">docker exec -it my-app-container sh # Run a one-off command
-weight: 500;">docker run --rm -it node:20-alpine node --version # Inspect a container's config, network, volumes
-weight: 500;">docker inspect <container_id> # Check resource usage
-weight: 500;">docker stats
# Open an interactive shell in a running container
-weight: 500;">docker exec -it my-app-container sh # Run a one-off command
-weight: 500;">docker run --rm -it node:20-alpine node --version # Inspect a container's config, network, volumes
-weight: 500;">docker inspect <container_id> # Check resource usage
-weight: 500;">docker stats
node_modules
.-weight: 500;">git
.env
*.log
dist
coverage
.DS_Store
README.md
-weight: 500;">docker-compose*.yml
node_modules
.-weight: 500;">git
.env
*.log
dist
coverage
.DS_Store
README.md
-weight: 500;">docker-compose*.yml
node_modules
.-weight: 500;">git
.env
*.log
dist
coverage
.DS_Store
README.md
-weight: 500;">docker-compose*.yml
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN -weight: 500;">npm ci
COPY . .
RUN -weight: 500;">npm run build # Produces /app/dist # Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN -weight: 500;">npm ci --only=production
COPY --from=builder /app/dist ./dist # Only copy built output
EXPOSE 3000
CMD ["node", "dist/server.js"]
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN -weight: 500;">npm ci
COPY . .
RUN -weight: 500;">npm run build # Produces /app/dist # Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN -weight: 500;">npm ci --only=production
COPY --from=builder /app/dist ./dist # Only copy built output
EXPOSE 3000
CMD ["node", "dist/server.js"]
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN -weight: 500;">npm ci
COPY . .
RUN -weight: 500;">npm run build # Produces /app/dist # Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN -weight: 500;">npm ci --only=production
COPY --from=builder /app/dist ./dist # Only copy built output
EXPOSE 3000
CMD ["node", "dist/server.js"]
// โ Wrong
const db = new Client({ host: 'localhost' }) // โ
Correct (use the Compose -weight: 500;">service name)
const db = new Client({ host: 'db' })
// โ Wrong
const db = new Client({ host: 'localhost' }) // โ
Correct (use the Compose -weight: 500;">service name)
const db = new Client({ host: 'db' })
// โ Wrong
const db = new Client({ host: 'localhost' }) // โ
Correct (use the Compose -weight: 500;">service name)
const db = new Client({ host: 'db' })
-weight: 500;">docker compose build --no-cache
-weight: 500;">docker compose build --no-cache
-weight: 500;">docker compose build --no-cache
-weight: 500;">docker logs <container_id>
-weight: 500;">docker logs <container_id>
-weight: 500;">docker logs <container_id>
ports: - "3001:3000" # Map to 3001 on host instead
ports: - "3001:3000" # Map to 3001 on host instead
ports: - "3001:3000" # Map to 3001 on host instead - Image โ A blueprint (like a class in OOP)
- Container โ A running instance of an image (like an object)
- Dockerfile โ The recipe for building an image
- Docker Compose โ A tool to orchestrate multiple containers together - Docker Desktop installed (includes Docker Compose)
- Basic terminal familiarity
- A project to containerize (we'll use examples for Node.js, Python, and a generic approach) - Dockerfile basics โ FROM, COPY, RUN, CMD and layer caching
- Building & running individual containers with -weight: 500;">docker build / -weight: 500;">docker run
- Docker Compose for multi--weight: 500;">service setups (app + database + cache)
- Environment variables and keeping secrets out of your images
- Dev/prod split using multiple Compose files
- Multi-stage builds for lean production images
- Debugging techniques when things go sideways - Docker volumes โ persisting data beyond the container lifecycle
- Docker networks โ custom networking between containers
- Kubernetes โ orchestrating containers at scale
- GitHub Actions + Docker โ CI/CD pipelines that build and push images automatically