Docker Fundamentals: Containers, Images, and Compose
A hands-on introduction to Docker — from understanding containers vs VMs to building production-ready images and multi-service stacks.
Table of Contents
Docker Fundamentals: Containers, Images, and Compose
Docker revolutionized how we build, ship, and run software. By packaging applications into portable containers, Docker eliminates the "works on my machine" problem and makes deployments reproducible and consistent.
Containers vs Virtual Machines
A container shares the host OS kernel, making it far lighter than a VM:
| Container | Virtual Machine | |
|---|---|---|
| Startup | Milliseconds | Minutes |
| Size | MBs | GBs |
| Isolation | Process-level | Hardware-level |
| Overhead | Minimal | Significant |
Core Concepts
Your First Dockerfile
# Use an official Node.js 20 slim image
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy dependency files first (layer caching optimization)
COPY package*.json ./
RUN npm ci --only=production
# Copy application source
COPY . .
# Expose the port
EXPOSE 3000
# Run as non-root user for security
USER node
CMD ["node", "server.js"]
Building and Running
# Build the image
docker build -t myapp:1.0 .
# Run a container
docker run -d \
--name myapp \
-p 3000:3000 \
-e NODE_ENV=production \
myapp:1.0
# View logs
docker logs -f myapp
# Execute a command inside
docker exec -it myapp sh
Docker Compose
Compose lets you define multi-service applications in a single YAML file:
version: '3.9'
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
depends_on:
db:
condition: service_healthy
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: pass
POSTGRES_USER: user
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 10s
retries: 5
volumes:
postgres_data:
# Start all services
docker compose up -d
# View all containers
docker compose ps
# Tear everything down
docker compose down -v
Multi-Stage Builds
Drastically reduce image size by separating build and runtime stages:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production (no dev dependencies, no build tools)
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]
The final image only contains what's needed to run — not the entire build toolchain.
Key Commands Reference
docker ps -a # list all containers
docker images # list images
docker rm $(docker ps -aq) # remove all stopped containers
docker rmi $(docker images -q) # remove all images
docker volume prune # remove unused volumes
docker system prune -af # nuclear cleanup
Docker is an essential tool in modern development. Master these fundamentals and you have a solid foundation for Kubernetes, CI/CD pipelines, and cloud deployments.