The problem Docker solves
"It works on my machine." Docker eliminates environment mismatch by packaging your application with everything it needs — the exact runtime version, system libraries, and configuration — into a single portable container.
Containers vs virtual machines
A VM emulates an entire computer including its own OS kernel — heavyweight, 512MB minimum. A container shares the host OS kernel, isolates only the application and dependencies. A container starts in milliseconds; a VM in minutes. You can run 50 containers on a server that fits only 5 VMs.
Images vs containers
A Docker image is a read-only template — like a class. A Docker container is a running instance of an image — like an object. Build an image once. Run it as many containers as you need.
The Dockerfile
A Dockerfile describes how to build an image. Each instruction creates a cached layer. Put rarely-changing instructions first: FROM node:20-alpine, WORKDIR /app, COPY package*.json ./, RUN npm ci, COPY . ., EXPOSE 3000, CMD ["node", "server.js"].
Docker Compose: multiple containers together
Most applications need a web container, a database container, and a cache container. Docker Compose defines them all in a docker-compose.yml file. docker compose up starts everything. docker compose down stops it. One command replaces a multi-step startup process.
Why Docker matters for deployment
Once your app runs in Docker locally, it runs identically in CI, staging, and production. Your pipeline builds the image, pushes to a registry, and the server pulls and runs it. This is the foundation of Kubernetes, AWS ECS, Google Cloud Run, and Fly.io.
Common mistakes
Running as root (add USER node). Storing data in containers (use volumes). Using the latest tag (pin to specific versions). Ignoring .dockerignore (always exclude node_modules, .git, .env).

