Published on

Docker for Developers - From Zero to Production

Authors

Introduction

"It works on my machine" — we've all said it. Docker eliminates this problem forever by packaging your application and all its dependencies into a container that runs identically everywhere.

In 2026, Docker knowledge is as fundamental as knowing Git. Here's everything you need.

What is Docker?

Docker is a platform for building and running containers — lightweight, isolated environments that include everything your app needs to run (code, runtime, libraries, config).

Your App + Runtime + Libraries + Config
         = Docker Image
              ↓ run
         Docker Container

Unlike VMs, containers share the host OS kernel — they start in seconds and use minimal resources.

Installation

# Install Docker Desktop (Mac/Windows/Linux)
# https://www.docker.com/products/docker-desktop

# Verify
docker --version
docker run hello-world

Your First Dockerfile — Node.js App

Dockerfile
# Start from an official Node.js image
FROM node:20-alpine

# Set working directory inside container
WORKDIR /app

# Copy package files FIRST (for layer caching)
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy the rest of the code
COPY . .

# Expose the port the app runs on
EXPOSE 3000

# Command to run the app
CMD ["node", "src/index.js"]
# Build the image
docker build -t my-node-app .

# Run the container
docker run -p 3000:3000 my-node-app

# Run in background (detached)
docker run -d -p 3000:3000 --name my-app my-node-app

Dockerfile for Python/FastAPI

Dockerfile
FROM python:3.12-slim

WORKDIR /app

# Install dependencies first (layer caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Docker Commands Cheatsheet

# Images
docker images                     # List all images
docker pull node:20                # Download an image
docker build -t my-app .           # Build from Dockerfile
docker rmi my-app                  # Remove an image

# Containers
docker ps                          # List running containers
docker ps -a                       # List all containers
docker run -p 3000:3000 my-app     # Run a container
docker start/stop/restart my-app   # Control a container
docker rm my-app                   # Remove a container
docker logs my-app                 # View logs
docker logs -f my-app              # Follow logs in real-time

# Exec — run commands inside container
docker exec -it my-app sh          # Open a shell inside container
docker exec my-app npm run migrate # Run a command

# Cleanup
docker system prune                # Remove unused resources
docker volume prune                # Remove unused volumes

Docker Compose — Multi-Container Apps

docker-compose.yml defines and runs multi-container applications:

docker-compose.yml
version: '3.9'

services:
  # Your Node.js API
  api:
    build: .
    ports:
      - '3000:3000'
    environment:
      NODE_ENV: development
      DATABASE_URL: postgresql://user:password@db:5432/mydb
      REDIS_URL: redis://redis:6379
    volumes:
      - .:/app           # Mount code for hot reload
      - /app/node_modules
    depends_on:
      - db
      - redis
    restart: unless-stopped

  # PostgreSQL database
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - '5432:5432'

  # Redis cache
  redis:
    image: redis:7-alpine
    ports:
      - '6379:6379'

volumes:
  postgres_data:
# Start all services
docker compose up

# Start in background
docker compose up -d

# Stop all services
docker compose down

# View logs for a service
docker compose logs api

# Rebuild after code changes
docker compose up --build

Environment Variables

Never hardcode secrets. Use .env files:

.env
NODE_ENV=development
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
JWT_SECRET=your-secret-key
docker-compose.yml
services:
  api:
    env_file:
      - .env  # Load all variables from .env

Add .env to .gitignore — never commit secrets!

Multi-Stage Builds — Smaller Production Images

Dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build  # TypeScript compile

# Stage 2: Production (much smaller!)
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist  # Only copy built files

EXPOSE 3000
CMD ["node", "dist/index.js"]

Multi-stage builds result in images 60-80% smaller — no dev dependencies, no source code.

.dockerignore — Don't Copy Unnecessary Files

.dockerignore
node_modules
npm-debug.log
.git
.env
.env.local
dist
coverage
*.md
Dockerfile
docker-compose.yml

Without this, Docker copies your node_modules into the build context — making builds slow.

Health Checks

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

Docker will automatically restart containers that fail health checks.

Conclusion

Docker is one of the highest-leverage tools a developer can learn. It eliminates environment inconsistencies, makes onboarding trivial (just run docker compose up), and is the foundation for modern deployment pipelines with Kubernetes and cloud platforms. Learn it once, use it forever.