For about a year my homelab was a pile of docker run commands I'd typed once and prayed I'd never have to type again. They lived in my shell history and in a text file that was already out of date. When the box rebooted last weekend, half of them didn't come back, and I spent an hour reconstructing flags from memory. That was the push I needed to move everything into a single docker-compose.yml.
why one file
You could argue for one Compose file per service, and there's a real case for it: blast radius, independent restarts, cleaner separation. I went the other way and put the lot in one file, because the honest truth is this is a house, not a datacentre. I want docker-compose up -d to bring back the entire stack after a reboot, and I want to read the whole topology on one screen.
What's in it: a reverse proxy, DNS, a couple of dashboards, the usual media bits, and a small Postgres that two of them share. Roughly nine services. That's about the limit of what reads comfortably in one file before you want to start splitting things.
version: "3"
services:
proxy:
image: traefik:1.2
command: --docker
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.toml:/traefik.toml
restart: unless-stopped
the bits that bit me
restart: unless-stopped on everything was the single most valuable change. That's what gets the stack back after a reboot without me touching it, which was the original sin I was trying to fix.
Volumes are where I lost the most time. I'd been letting Docker manage anonymous volumes, which is fine right up until you want to know where your data actually lives. I moved everything to bind mounts under one directory so I can back it up with a single rsync. Named volumes are tidier; bind mounts are honest about where the bytes are, and for a homelab I'll take honest.
The networking caught me too. By default Compose puts everything on one project network and lets services find each other by name, which is exactly what I wanted, so the proxy can reach dashboard without me hardcoding IPs. Once I stopped fighting that and leaned into it, half my config disappeared.
It's not elegant and a purist would split it up. But it boots clean, it fits on a screen, and the whole house came back on its own after I pulled the plug to test. Good enough is a real engineering target, and this clears it.