Docker is a platform that packages applications and their dependencies into lightweight, isolated containers that run consistently across any environment. A container includes everything needed to run software — code, runtime, libraries, system tools — without the overhead of a full virtual machine.
Containers vs. virtual machines
Virtual machines emulate entire operating systems, each with its own kernel. Containers share the host’s kernel and isolate processes using Linux namespaces and cgroups. This makes containers:
- Faster to start: Seconds, not minutes
- Smaller: Megabytes, not gigabytes
- More efficient: Run dozens of containers on hardware that might handle a handful of VMs
Key concepts
Image: A read-only template that defines what goes into a container. Built from a Dockerfile, layered and cached for efficiency.
Container: A running instance of an image. Ephemeral by default — when it stops, changes are lost unless you use volumes.
Dockerfile: A text file with instructions for building an image:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Docker Compose: A tool for defining multi-container applications in a single YAML file. Run a web server, database, and cache together with docker compose up.
Registry: A storage and distribution service for images. Docker Hub is the default public registry. Companies run private registries (AWS ECR, GitHub Container Registry).
Why developers use Docker
- “Works on my machine” solved: The container runs the same everywhere — your laptop, CI/CD, staging, production.
- Dependency isolation: Different projects can use different versions of Node, Python, databases without conflicts.
- Reproducible builds: A Dockerfile pins every dependency.
docker buildproduces the same image whether you run it today or next year. - Microservices: Each service runs in its own container with its own dependencies and scaling characteristics.
Common commands
docker build -t myapp . # Build an image
docker run -p 3000:3000 myapp # Run a container
docker ps # List running containers
docker logs <container> # View container logs
docker exec -it <container> sh # Shell into a running container
docker compose up -d # Start services in background
Best practices
- Use specific base image tags (
node:20-alpine, notnode:latest) - Multi-stage builds to keep final images small
- Don’t run containers as root
- Use
.dockerignoreto excludenode_modules,.git, and other unnecessary files - One process per container
Generate Dockerfiles with the Dockerfile Generator, create Compose files with the Docker Compose Generator, or reference commands with the Docker Cheatsheet.