Why Docker Has Become a Developer Essential
Docker solves one of the oldest problems in software: "It works on my machine." By packaging an application along with its entire runtime environment — OS libraries, dependencies, config — into a portable container, Docker ensures your app runs the same way everywhere: your laptop, a teammate's machine, a CI server, or a production cloud.
You don't need to be a DevOps engineer to benefit from Docker. This guide focuses on the developer use cases that matter most day-to-day.
Core Concepts in Plain English
Images
An image is a read-only blueprint for a container. Think of it like a class definition. Images are built from a Dockerfile and stored in registries like Docker Hub.
Containers
A container is a running instance of an image — the actual executing process, isolated from the host system. You can run many containers from the same image.
Dockerfile
A text file with instructions that tell Docker how to build your image.
# Example Dockerfile for a Node.js app
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Volumes
Containers are ephemeral — data written inside a container disappears when it stops. Volumes are how you persist data (databases, uploaded files) outside the container lifecycle.
Essential Docker Commands
| Command | What It Does |
|---|---|
docker build -t myapp . | Build an image from the current directory's Dockerfile |
docker run -p 3000:3000 myapp | Run a container, mapping port 3000 |
docker ps | List running containers |
docker logs <container_id> | View container output/logs |
docker exec -it <id> sh | Open an interactive shell inside a running container |
docker stop <id> | Gracefully stop a container |
docker system prune | Remove unused containers, images, and networks |
Docker Compose: Managing Multi-Container Apps
Real applications often need multiple services — a web app, a database, a cache. Docker Compose lets you define and run them all with a single docker-compose.yml file.
version: "3.9"
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
DATABASE_URL: postgres://user:pass@db:5432/mydb
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
pgdata:
With this file, docker compose up starts both services, and docker compose down tears them down cleanly.
Developer Workflow Tips
- Use bind mounts during development so code changes inside the container reflect immediately without rebuilding the image:
-v $(pwd):/app. - Use multi-stage builds to keep production images small — build in one stage, copy only the output to the final image.
- Add a .dockerignore file (like .gitignore) to exclude
node_modules,.git, and other unnecessary files from the build context. - Tag images meaningfully:
myapp:1.2.0instead of relying solely onlatest.
Common Gotchas
- Container networking: services in the same Compose file communicate by service name, not
localhost. - File permissions can differ between macOS/Windows hosts and Linux containers — especially relevant with volume mounts.
- Don't bake secrets into images. Use environment variables or secret management tools instead.
Next Steps
Once you're comfortable with the basics, explore Docker Desktop's Dev Containers feature, which integrates deeply with VS Code for a fully containerized development environment. For production, look into Kubernetes or managed container services like AWS ECS, Google Cloud Run, or Railway.