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 indocker-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 theservices:
block; specifying a version is no longer needed or recommended.
Also keep in the that the “host” port number, on the left-hand side of the colon (i.e. the
8080
before:
), is the one that is exposed to the host machine, and it’s the “access” port you will use to interact with the Docker service, from your host machine, outside the container.
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’slocalhost:8080
points tocontainer: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):
- 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 thelatest
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.
- 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.
- Run it:
Use the docker compose
command to spin up the container:
docker compose up
- (Use
docker compose
, NOTdocker-compose
; the hyphenated one is deprecated.)
- 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 thendocker 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 withsudo
. - 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.