For about a year my homelab was a museum of how I felt on any given weekend. Some services ran as raw docker run commands buried in shell history. Some had their own little Compose files in scattered directories. One important thing was, embarrassingly, a systemd unit I'd forgotten I wrote. When the box rebooted after a power cut in May, I spent an evening rediscovering my own infrastructure like an archaeologist.
So I did the obvious thing and pulled everything into a single docker-compose.yml. DNS, the reverse proxy, the media stack, the dashboards, the bits that back up the other bits. One file, version-controlled, one docker compose up -d to bring the whole house up. It felt wonderful for about a fortnight.
The problem with one file for everything is that everything shares a fate. Restart the stack to add a service and you've just bounced your DNS, which means for thirty seconds nothing in the house can resolve a name, which means the very thing you were calmly editing has now woken up three other people. I learned this by doing it at half nine on a Tuesday and being asked, pointedly, why the television had stopped working.
The fix wasn't to abandon Compose, it was to stop treating the house as one blast radius. I split it by how much I'd care if it went down:
# infra/docker-compose.yml -- DNS, reverse proxy. Touch rarely.
# media/docker-compose.yml -- the *arr stack, jellyfin. Reboot freely.
# apps/docker-compose.yml -- dashboards, toys, half-finished ideas.
Three stacks, three directories, each with its own .env, sharing one external Docker network so the proxy can still see everything:
networks:
edge:
external: true
Now I can break the toys without taking down name resolution, and the infra stack only gets restarted when I genuinely mean it. The "whole house in one file" dream was the right instinct and the wrong granularity. The unit of deployment should be the unit of acceptable failure, and in a house, those are not the same thing. Obvious in hindsight. Most good boundaries are.