"It works on my machine" is one of the oldest problems in software. Different operating systems, library versions and settings cause bugs that appear only on the server. Docker solves this by bundling your app with everything it needs into a container that behaves the same everywhere.
Key takeaways
- A Dockerfile describes how to build an image; running an image creates a container.
- Containers are lighter and faster than virtual machines.
- Docker Compose runs multi-service apps, such as an API plus a database, with one command.
- Containers make deployments and onboarding repeatable.
Containers vs virtual machines
| Aspect | Virtual machine | Container |
|---|---|---|
| Includes | Full guest operating system | App and libraries only |
| Size | Gigabytes | Often megabytes to a few hundred MB |
| Start time | Minutes | Seconds or less |
| Isolation | Strong (separate OS) | Good (shares the host kernel) |
| Best for | Different operating systems, strong isolation | Packaging and running applications |
Core concepts
- Image: a read-only template with your code, runtime and dependencies.
- Container: a running instance of an image.
- Dockerfile: the recipe to build an image.
- Registry: a store for images, such as Docker Hub or your cloud provider's registry.
- Volume: storage that survives when a container is removed, used for databases and uploads.
- Network: how containers talk to each other and to the outside world.
Install and test
Install Docker Desktop (Windows, macOS) or Docker Engine (Linux), then check that it works:
docker --version
docker run hello-world
A real example: containerise a Node.js API
Imagine a small API with package.json and server.js. Create a file named Dockerfile:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
USER node
CMD ["node", "server.js"]
Add a .dockerignore so you do not copy junk into the image:
node_modules
.git
.env
npm-debug.log
Build and run:
docker build -t my-api .
docker run -d --name my-api -p 3000:3000 --env-file .env my-api
curl http://localhost:3000
Why it is written this way: copying package*.json first lets Docker cache the installed dependencies so rebuilds are fast; running as the non-root node user is safer; and secrets come from environment variables, not the image.
Docker Compose: run several services together
Real apps need a database, cache or queue. A docker-compose.yml describes all of them:
services:
api:
build: .
ports: ["3000:3000"]
env_file: .env
depends_on: [db]
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
docker compose up -d # start everything
docker compose logs -f # watch logs
docker compose down # stop and remove containers
Essential commands
| Command | What it does |
|---|---|
docker ps / docker ps -a | List running / all containers |
docker images | List images |
docker logs -f NAME | Follow a container's logs |
docker exec -it NAME sh | Open a shell inside a running container |
docker stop NAME / docker rm NAME | Stop and remove a container |
docker system prune | Remove unused containers, networks and images (careful) |
Best practices
- Use small base images such as Alpine or slim variants.
- Pin versions (node:22-alpine, postgres:16) so builds are repeatable.
- Do not run as root and do not bake secrets into images.
- Use multi-stage builds to keep build tools out of the final image.
- Add health checks and a restart policy.
- Scan images for vulnerabilities and update base images regularly. See our security guide for patching habits.
Where Docker fits in deployment
Containers run on a single server with Docker Compose, on managed container platforms and in Kubernetes for large systems. Many teams start with Compose on a VPS and move to a managed platform later. Our projects often run in Docker on a VPS or cloud provider; compare options in AWS vs Azure vs Google Cloud and plan the move with the cloud migration guide.
When not to use Docker
- A tiny static website that needs no server.
- When your team cannot yet maintain the extra layer: start simple.
- Apps that need direct access to special hardware or desktop graphics.
Learn more
New to Git and workflows? Read Git and GitHub for beginners and the web developer roadmap. Need a team to containerise and deploy your product? See our custom software service or contact us.



