Docker has revolutionized how developers build, ship, and run applications. Containerization eliminates "it works on my machine" problems and makes development environments reproducible. But getting started with Docker can be intimidating.

This guide covers Docker from basics to advanced compose files, helping you containerize your applications and streamline your development workflow.

What is Docker?

Docker is a platform for developing, shipping, and running applications in containers. Containers are lightweight, standalone packages that include everything needed to run an application: code, runtime, libraries, and settings.

Key Benefits:

Installing Docker

macOS

# Using Homebrew
brew install --cask docker

# Or download from docker.com
        

Linux

# Ubuntu/Debian
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

# Add your user to docker group
sudo usermod -aG docker $USER
        

Windows

Download Docker Desktop from docker.com and run the installer.

Docker Basics

Images and Containers

Analogy: Images are like classes, containers are like objects instantiated from those classes.

Key Commands

# Pull an image
docker pull nginx:latest

# Run a container
docker run -d -p 80:80 --name my-nginx nginx

# List running containers
docker ps

# List all containers
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

# Remove an image
docker rmi nginx:latest
        

Dockerfile: Building Your Own Images

A Dockerfile is a script for building Docker images. It defines the base image, dependencies, and how to run your application.

Node.js Application Dockerfile

# Use official Node.js image
FROM node:20-alpine

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm install --production

# Copy application code
COPY . .

# Expose port
EXPOSE 3000

# Start application
CMD ["node", "server.js"]
        

Python Application Dockerfile

# Use official Python image
FROM python:3.13-slim

# Set working directory
WORKDIR /app

# Copy requirements
COPY requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY . .

# Expose port
EXPOSE 8000

# Start application
CMD ["python", "app.py"]
        

Building an Image

docker build -t my-app:1.0 .
        

Docker Compose: Multi-Container Applications

Docker Compose defines and runs multi-container Docker applications. It's perfect for development with database, cache, and app containers.

docker-compose.yml Example

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
      - redis
    environment:
      - NODE_ENV=development
      - DB_HOST=db
      - REDIS_HOST=redis

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=appuser
      - POSTGRES_PASSWORD=apppassword
      - POSTGRES_DB=appdb
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine

volumes:
  postgres_data:
        

Compose Commands

# Start all services
docker-compose up

# Start in background
docker-compose up -d

# Stop services
docker-compose down

# Rebuild and start
docker-compose up --build

# View logs
docker-compose logs -f app

# Scale services
docker-compose up -d --scale app=3
        

Development Workflow

1. Use Named Volumes

Named volumes persist data even when containers are removed. Use them for databases and code mounts.

volumes:
  - node_modules:/app/node_modules
  - postgres_data:/var/lib/postgresql/data
        

2. Mount Source Code

Mount your source code for hot-reloading during development.

services:
  app:
    volumes:
      - .:/app
      - /app/node_modules
        

3. Use .dockerignore

Exclude unnecessary files from builds to speed up builds and reduce image size.

node_modules
.git
.env
dist
*.log
        

Production Optimizations

Multi-Stage Builds

Reduce image size by building in one stage and copying only artifacts to the final stage.

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 80
CMD ["node", "dist/server.js"]
        

Use Alpine Images

Alpine Linux images are smaller and more secure than full images.

FROM node:20-alpine  # ~180MB
# Instead of
FROM node:20         # ~900MB
        

Optimize Layer Caching

Order Dockerfile commands to cache dependencies and only rebuild when they change.

COPY package.json package-lock.json ./
RUN npm install  # Cached unless package.json changes
COPY . .         # Rebuild only when code changes
        

Networking

Bridge Networking

Default networking mode where containers communicate via a bridge.

docker network create my-network
docker run --network my-network my-app
        

Host Networking

Use host networking for performance when networking overhead isn't needed.

docker run --network host my-app
        

Docker for CI/CD

GitHub Actions with Docker

name: Docker CI
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build Docker image
        run: docker build -t my-app:$GITHUB_SHA .
      - name: Run tests
        run: docker run my-app:$GITHUB_SHA npm test
      - name: Push to registry
        run: docker push my-registry/my-app:$GITHUB_SHA
        

Docker Tips and Tricks

1. Clean Up Unused Resources

# Remove stopped containers
docker container prune

# Remove unused images
docker image prune

# Remove unused volumes (use with caution!)
docker volume prune

# Remove everything
docker system prune
        

2. View Container Logs

# View logs
docker logs my-container

# Follow logs
docker logs -f my-container

# View last 100 lines
docker logs --tail 100 my-container
        

3. Execute Commands in Running Container

# Interactive shell
docker exec -it my-container sh

# Run single command
docker exec my-container npm test
        

4. Copy Files Between Host and Container

# Copy from host to container
docker cp local_file.txt my-container:/app/

# Copy from container to host
docker cp my-container:/app/remote_file.txt ./
        

5. Check Container Resource Usage

docker stats
        

Common Pitfalls to Avoid

1. Running Everything as Root

Use non-root users for security.

RUN addgroup -g 1000 appuser && \
    adduser -D -u 1000 -G appuser appuser
USER appuser
        

2. Hardcoding Environment Variables

Pass environment variables via docker-compose or -e flag, never hardcode secrets.

3. Building Giant Images

Use multi-stage builds, Alpine images, and .dockerignore to keep images small.

4. Not Cleaning Up

Regularly prune unused images and containers to reclaim disk space.

2026 Docker Updates

Docker 27.0

Learning Path

Week 1

Week 2

Week 3+

Conclusion

Docker transforms how developers work by providing consistent, reproducible environments. Start with simple containers using docker-compose, then optimize for production with multi-stage builds and Alpine images.

Don't feel overwhelmed — Docker has a learning curve, but once mastered, it becomes an essential part of your development toolkit.

Affiliate Disclosure

This article contains affiliate links to Docker registries and tools. If you click through and sign up or purchase, I may earn a commission at no additional cost to you. I use Docker daily and recommend it to all developers.