Docker Tutorial for Beginners
I get this how frustrating Docker can be. Docker feels way more intimidating than it needs to when you first bump into it, and nobody explains the basics without hand-waving through all the stuff that's actually confusing.
Here's a no-BS, jargon-free walkthrough of Docker, containers, Docker Compose, and every basic command you'll actually use as a new developer. Cut through the hype, skip the fluff, and get your first web app running in Docker fast.
1. WTF Is Docker (And Why Not Just Run the App)?
Docker is a way to package up a web app (or any app) with all its dependencies—think of it as putting your app and its environment into a shipping container so it works exactly the same on your computer as it does on anyone else's.
What Is Docker really?
You can also think of Docker as a containerization platform that lets you package your app and its environment into a portable "container" so it runs exactly the same on any computer—no more "works on my machine" headaches—with many base images (boilerplate, pre-fab Linux instances with specialized code and packages installed on it) to chose from in Docker Hub.
- Why not just install everything on your machine?
- You'd pollute your system with dependencies.
- Everyone's computer is slightly different—works on my machine, fails on yours.
- Docker makes the whole app portable and less error-prone.
2. Docker Desktop: What Is It?
Docker Desktop is a user-friendly GUI and command-line tool for managing Docker on Windows and Mac. It comes bundled with everything you need to get started.
Installing Docker on Ubuntu
You can use Ubuntu's apt package manager to install Docker:
sudo apt update
sudo apt install docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
Log out and back in so your user can run Docker without sudo. You can also install Docker Desktop on Ubuntu using the latest DEB package.
3. How Is Docker Different from a Virtual Machine (VM)?
- VM: Pretends to be a full computer—has its own OS, boots up slow, uses a lot of RAM and CPU.
- Docker Container: Shares your system's kernel (the core part of the OS), but isolates the app. Way lighter and faster.
- TL;DR: VM = full computer in a box. Docker = just enough isolation to keep things tidy and reproducible.
4. What Is a Docker Container?
- A container is just a running instance of your app, with all its stuff inside, isolated from your system.
- Think of it like a mini-computer just for your app, but running inside your actual computer.
5. What's a Dockerfile vs. Docker Compose?
- Dockerfile: Recipe for how to build one container (like: get this OS, install Node.js, copy in app code, etc).
- docker-compose.yml: A way to describe and run multiple containers at once (your web app, plus maybe a database, plus whatever else).
6. The Anatomy of a docker-compose.yml (Service Block Breakdown)
Let's say you've got a minimal docker-compose.yml that looks something like this:
services:
web:
build: .
ports:
- "8080:80"
volumes:
- .:/usr/src/app
environment:
- NODE_ENV=development
NOTE: The version: field in docker-compose.yml is now deprecated and should be omitted—Docker Compose automatically detects the correct version based on your file's syntax. Just start your file with the services: block; specifying a version is no longer needed or recommended.
Let's break down the key parts:
Field | WTF It Means |
---|---|
services | A list of "containers" (your web app, database, etc). |
web | Name of one service/container. Call it whatever you want. |
build | Build a Docker image using the Dockerfile in this directory. |
image | (Alternative to build): Use an existing image from Docker Hub. |
ports | Maps a port on your computer to the port in the container. |
volumes | Shares files/folders between your machine and the container. |
environment | Set environment variables inside the container. |
Common confusion:
- 8080:80 means: hostPort:containerPort → So, your computer's localhost:8080 points to container:80.
7. What Do You Actually Need to Do, Step-by-Step?
Assuming you've got Docker Desktop installed (Mac, Windows) or Docker Engine (Linux):
1. Get or write a Dockerfile
Here's a dead-simple one for a static site:
FROM nginx:alpine
COPY . /usr/share/nginx/html
Warning:
Using nginx:alpine (with no version number) will always pull the latest version of the Alpine-based Nginx image on Docker Hub.
Docker 'Latest Tag' Issue
It's best practice not to use the implicit latest tag for production or anything you want to be stable. Here's why:
- nginx:alpine defaults to the latest version of Alpine-based Nginx.
- If the upstream image updates, your build could break or introduce security issues without warning.
- For stability, always pin to a specific version (e.g., nginx:1.27-alpine).
This can lead to unexpected breakage if a new major or breaking change is released upstream. For more predictable builds, always pin your image to a specific version tag, like nginx:1.27-alpine:
FROM nginx:1.27-alpine
COPY . /usr/share/nginx/html
That way, you avoid nasty surprises and keep your builds consistent.
2. Write a docker-compose.yml:
Here's some example YAML for an Nginx Docker service labelled as web:
services:
web:
build: .
ports:
- "8080:80"
volumes:
- .:/usr/share/nginx/html
- This runs Nginx (a fast web server), serves your static files, and lets you access them on http://localhost:8080.
3. Run it:
Use the docker compose command to spin up the container:
docker compose up
- (Use docker compose, NOT docker-compose; the hyphenated one is deprecated.)
4. See it in action:
Open your browser to http://localhost:8080.
8. Tips for Sanity
- If something changes and you're not seeing updates:
Try docker compose down and then docker compose up --build to force a rebuild.
- You can stop all containers with Ctrl+C in the terminal window you started them in.
- You can see running containers with docker ps.
9. The Frustrating Gotchas
- File Permissions: Sometimes your files inside the container are "root" (admin) owned—can be annoying.
- Networking: If you need to talk between containers (like web app ↔ database), Docker Compose gives them private names (e.g. db:5432).
- Volumes: If you use a volume, files you change on your computer should update inside the container, but sometimes cache issues crop up.
10. TL;DR Cheat Sheet
- Docker ≠ VM: It's way lighter.
- Compose = runs multiple containers/services at once.
- Most fields in the compose YAML are self-explanatory once you realize:
- build = "build my app here",
- image = "use this existing app",
- ports = "make it accessible from my computer",
- volumes = "keep my code in sync with the container".
Troubleshooting Common Docker Errors
- "Port is already allocated" — Use another port, or stop the conflicting container.
- "Permission denied" — You probably need to add your user to the docker group or run with sudo.
- Files not updating? — Rebuild your image, or check your volumes mapping.
- App isn't reachable? — Double-check your ports mapping and firewall.
Conclusion
Docker and Docker Compose can feel overwhelming at first, but you only need a handful of commands and config options to get productive. Stick to the basics: Dockerfile for images, docker compose for orchestration, and Docker Hub for pulling pre-built images. Once you're up and running, everything else is just incremental learning.