Docker Compose lets you describe a whole app — its code, storage, and networking — in one readable file, and bring it up or down with a single command.
It's the pattern behind nearly every modern self-hosting project you'll encounter. Understand this one tool, and installation instructions like "just run docker compose up" stop being magic and start being routine.
The problem Compose solves
Without containers, installing an app on a server means installing its language runtime, its database, its dependencies — all directly onto your system, where versions collide and uninstalls leave debris. Docker packages each app with everything it needs. Compose goes one step further: it defines groups of containers that work together, so a blog plus its database is one unit, not two separate puzzles.
A real example
Here's a complete, working Compose file for Uptime Kuma, the monitoring tool from our essential repos list. One file, one command, fully running app:
# docker-compose.yml
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- uptime-kuma-data:/app/data
ports:
- "3001:3001"
restart: unless-stopped
volumes:
uptime-kuma-data:
Read it top to bottom: pull this image, keep the data in a named volume, publish port 3001, restart automatically if the server reboots. Every Compose file you'll ever meet is variations on those five ideas.
Bring it up
# In the directory holding the file:
docker compose up -d
# See what's running:
docker compose ps
# Read logs when something looks wrong:
docker compose logs -f
Point a browser at http://your-server-ip:3001 and Uptime Kuma is waiting for you. That's the entire installation.
One folder per app, always
The habit that keeps a server sane: every app lives in its own directory with its own compose file.
/opt/
uptime-kuma/
docker-compose.yml
vaultwarden/
docker-compose.yml
immich/
docker-compose.yml
Each project is isolated — its own containers, its own network, its own volumes. Updating or removing one app never touches another.
Updates without fear
docker compose pull # fetch newer images
docker compose up -d # recreate only what changed
Because the data lives in named volumes, the containers themselves are disposable. A broken update rolls back the same way: pin the previous image tag, up -d again.
What to run first
If you're following the learning path, good first Compose projects are Uptime Kuma (one container, instant gratification), then a two-container app with a database once you're comfortable. Platforms like Coolify build a whole UI on top of this same pattern — the Compose mental model transfers directly.