Ramblings of an aging IT geek
← Ramblings of an aging IT geek
homelab

the homelab as one stack, profiles and all

A second pass at running the whole house from Docker Compose, this time leaning on profiles and healthchecks to keep the stack honest.

A server rack with neat cabling

I wrote a few days ago about collapsing the homelab into a single Compose project. That got everything into one place and one git repo, which was most of the battle. What it didn't solve was the fact that not everything in the house wants to be running all the time, and that "the container is up" is not the same as "the service works." This post is the second pass, where Compose stops being a glorified docker run and starts earning its keep.

profiles, so I'm not running everything always

Some services are core: DNS, the reverse proxy, monitoring. Others are occasional: a media transcoder I only want during the evening, a download client, a thing I spun up for one experiment. I don't want all of them eating RAM around the clock, but I also don't want them living in a separate file I'll forget about.

Compose profiles are the answer. Tag a service and it only starts when you ask for its profile:

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    profiles: [media]
    restart: unless-stopped
  prometheus:
    image: prom/prometheus:latest
    restart: unless-stopped

docker compose up -d brings up the always-on core. docker compose --profile media up -d adds the media stack on top. Everything stays in one file, version-controlled together, but I decide at runtime what's actually awake. No more commented-out blocks, which is the homelab equivalent of leaving a note on the fridge that nobody reads.

A homelab shelf with a switch and small servers

healthchecks, because "up" lies

The thing that genuinely improved my evenings was healthchecks. A container can be running while the application inside it is wedged, and Compose will cheerfully report it as up. So I tell each service how to prove it's actually alive:

  pihole:
    image: pihole/pihole:latest
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "dig", "+short", "@127.0.0.1", "pi.hole"]
      interval: 30s
      timeout: 5s
      retries: 3

Now docker compose ps tells me the truth. A wedged container shows unhealthy instead of a reassuring Up 3 days, and anything that depends_on it with condition: service_healthy waits for the real thing rather than the mere existence of a process. The first time DNS resolution in the house silently broke while the container claimed to be fine, I added these everywhere and never looked back.

the boring conclusion

None of this is sophisticated. It's profiles and healthchecks, two features that have been in Compose for ages and that I'd ignored because the stack "worked." The difference is that the house now tells me when it's lying to me, and I can run the heavy bits only when I want them. For four containers and a Pi, that's about the right amount of cleverness. Any more would be a hobby in its own right, and I already have enough of those.