Learn Docker with a practical path: images, containers, Dockerfile builds, compose stacks, and the workflow developers use in real projects.
Learning Docker is less about memorizing commands and more about understanding how applications are packaged, shipped, and run the same way on your laptop, in CI, and in production. If you searched for a docker tutorial because tutorials you tried skipped the mental model, this guide walks from first container to a repeatable workflow you can extend.
What Docker solves
Before containers, “it works on my machine” was a scheduling problem. Teams shipped source code plus a README of system dependencies. Versions drifted. CI used different libraries than staging. Docker does not replace virtual machines entirely, but for most application workloads it gives you a filesystem snapshot (an image) and a running instance (a container) that shares the host kernel.
You get:
- Isolation without booting a full OS per app
- Reproducible builds via a Dockerfile checked into git
- Portable artifacts you can push to a registry and pull anywhere
That portability is why docker tutorials show up in cloud, DevOps, and backend learning paths alike.
Core vocabulary
| Term | Meaning |
|---|---|
| Image | Read-only template with app + dependencies |
| Container | Running (or stopped) process from an image |
| Dockerfile | Recipe that builds an image |
| Registry | Storage for images (Docker Hub, ECR, GHCR) |
| Volume | Persistent storage outside the container layer |
| Network | How containers reach each other and the host |
If you remember only one idea: images are immutable layers; containers are disposable.
Install and verify
Install Docker Desktop (macOS/Windows) or the engine packages on Linux. Verify:
docker version
docker run hello-world
hello-world downloads a tiny image, runs it, prints a message, and exits. You have just completed the full pull-run lifecycle.
Your first interactive container
Run an Ubuntu shell you can type into:
docker run -it --rm ubuntu:24.04 bash
Inside the container, cat /etc/os-release shows Ubuntu even if your host is different. Exit with exit; --rm deletes the container automatically.
Flags explained briefly:
-itattaches a terminal for interactive programs--rmremoves the container on exit (great for experiments)ubuntu:24.04isrepository:tag
Building a real image
Suppose you have a small Node API in ./app. A minimal Dockerfile:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "server.js"]
Build and run:
docker build -t my-api:local .
docker run --rm -p 3000:3000 my-api:local
-p 3000:3000 maps host port 3000 to the container. Visit http://localhost:3000.
Layer caching matters
Docker caches each Dockerfile step. Copy dependency manifests before application source so npm ci reruns only when package.json changes. This single ordering trick speeds daily builds more than almost any other beginner tweak.
docker compose for multi-service apps
Most tutorials stop at one container. Real stacks need a database, cache, or worker. Docker Compose describes multiple services in YAML.
Example compose.yaml:
services:
api:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Start everything:
docker compose up --build
Compose creates a private network; the API reaches Postgres at hostname db. Volumes keep database files when containers restart.
Everyday commands you will actually use
docker ps # running containers
docker ps -a # all containers
docker images # local images
docker logs <id> # stdout/stderr
docker exec -it <id> sh # shell into running container
docker stop <id>
docker rm <id>
docker rmi <image>
docker system prune # cleanup unused objects (careful in shared machines)
Learn docker compose logs -f api early—it saves hours when something fails on boot.
Development workflow patterns
Bind mounts let you edit code on the host while a container runs dependencies:
services:
api:
build: .
volumes:
- .:/app
command: npm run dev
The container sees live file changes. Pair this with a .dockerignore so you do not copy node_modules from the host into the build context.
For production, stop bind-mounting source. Build an image in CI, scan it, push to a registry, deploy by digest—not :latest tags you cannot reproduce.
Security and hygiene basics
Containers are not automatic security. Treat them like processes:
- Do not run as root when you can add a
USERline in the Dockerfile - Pin base image tags (
node:22-alpine, notnode:latest) - Scan images in CI for known CVEs
- Never bake secrets into images; inject via environment or a secrets manager at runtime
Where learners get stuck
- Confusing images and containers — You can have many containers from one image.
- Huge build contexts — Add
.dockerignorefornode_modules,.git, and build artifacts. - Publishing ports — The app must listen on
0.0.0.0, not only127.0.0.1, inside the container. - File permissions on volumes — Linux bind mounts may create root-owned files; plan UIDs or named volumes.
Next steps after this tutorial
- Push images to a registry and deploy on AWS ECS, Fly.io, or a Kubernetes cluster
- Add health checks (
HEALTHCHECKin Dockerfile or compose) - Learn multi-stage builds to keep production images small
- Read official docs on BuildKit cache mounts when builds slow down
Docker becomes intuitive once you treat containers as cattle (replaceable) and images as golden templates. Run them often, delete them freely, and let your Dockerfile be the contract your team agrees on.
FAQ
Do I still need to learn Kubernetes? Not on day one. Compose covers local and small deployments. Learn orchestration when you need rolling updates across many nodes.
Is Docker free? The engine is open source. Docker Desktop licensing applies to larger enterprises; Linux engine installs are commonly used without Desktop.
How is Docker different from a VM? VMs virtualize hardware and boot full operating systems. Containers share the host kernel and isolate processes with namespaces and cgroups—lighter and faster to start.
Can I run Docker in CI? Yes. Most pipelines use Docker or BuildKit to build images, run tests in containers, and push artifacts before deploy.
Comments
Loading comments…