Building a small homelab that survives upgrades
Every homelab starts with one box and a promise that this time the setup will be documented. Two years later the promise is gone and the box is running ninety-three containers. Here is what kept mine alive.
One directory per service
A service is a directory: compose.yaml, .env, and a data/ folder. Nothing else. No named volumes, no files outside the directory, no "it is easier if I just put this in /etc".
/opt/example/
compose.yaml
.env # chmod 600
data/ # bind mounted
When something breaks, the entire state of the service is in one place. When something needs to move, it is a rsync away.
Pinned everything
Floating tags are a promise that today's deployment is not reproducible tomorrow. Pin image tags, pin script versions, and write the version into the compose file so a rollback is a one-line edit.
Health checks are not optional
A container that is running is not a container that works. Every service gets a health check that hits a real endpoint, and every reverse proxy points at the health check, not at the container name.
Backups that are tested
An untested backup is a hope. Once a month, restore one database into a scratch directory and query it. If the restore takes more than ten minutes, the backup is wrong.
The boring conclusion
Homelabs fail from undocumented changes, not from bad hardware. Write the command you ran in the service directory. Future you will read it.