/var/www/xnett.org/containers/what-runs-in-my-homelab-containers.md
[CONTAINERS]What runs in my homelab containers
Home Assistant, Frigate, Jellyfin, Sonarr, Nextcloud, Open WebUI and an active/active Ops Center: the services on my two Docker hosts and the rules I run them by.
- author:
- Rolf
- date:
- 2026-09-29 22:48
- read:
- ~3 min
Almost every service in the homelab is a container on one of two Docker hosts. They are plain VMs running Docker Engine and Compose, no Kubernetes, and each host has a separate data volume so the OS disk and the application data can be handled independently.
The two hosts
| Docker host #1 | Docker host #2 | |
|---|---|---|
| OS | AlmaLinux | Ubuntu |
| Size | 8 vCPU, 12 GB RAM | 4 vCPU, 8 GB RAM |
| Data | 250 GB data volume | 250 GB data volume |
| Flavour | Home automation, media streaming, AI, monitoring | Edge, cloud, downloads, platform |
Stacks are Compose projects. Some are managed from Portainer, the rest straight from the host. Either way the rule is the same: config in files, data on the data volume, nothing important only inside a container.
Home automation
- Home Assistant (container, host networking). It started life as a Supervised install on its own VM and was migrated to a container, which made backups and upgrades much less dramatic.
- Mosquitto as the MQTT broker for everything that speaks MQTT.
- ring-mqtt bridges a Ring doorbell into MQTT, so a ding in Home Assistant triggers a notification and a short clip from the live stream, without a cloud subscription.
- Frigate + go2rtc record the Wi-Fi cameras to local disk. Detection is off; it's a recorder, not a smart NVR, which keeps CPU use sane without a Coral.
Media
- Jellyfin streams the library.
- Sonarr manages TV series, Jackett provides indexers, Transmission does the downloading.
- The library itself lives on the NAS and is mounted over NFS into the containers, so the Docker host never holds media.
With linuxserver.io images, the download client's default download directory is inside the container. If it isn't mapped to the NFS mount, downloads "succeed" into a path nobody reads. Check the client's configured download dir, not just the volume mapping.
Cloud and personal data
- Nextcloud with MariaDB for files and sync.
- A self-hosted password manager.
These are the services where the backups are tested, not assumed.
AI
- Open WebUI as the chat front end.
- Ollama and ComfyUI run on the GPU laptop (RTX 3080 Laptop, 16 GB VRAM) in containers, and Open WebUI talks to them over the LAN. Image generation goes from Open WebUI to ComfyUI.
The GPU box is a laptop on purpose: it's the most GPU per watt and per krone I could get, and it's idle most of the day.
Edge and platform
- A reverse proxy in front of every internal web service. It terminates TLS with real Let's Encrypt certificates issued via DNS-01, so internal services get valid HTTPS without being exposed.
- WireGuard for remote access, with Cloudflare DDNS keeping the endpoint current.
- Portainer (server on one host, agents on both) for when a UI is quicker than a shell.
- Uptime Kuma for endpoint checks from the outside-in.
- Watchtower for automatic image updates on the services where that's acceptable.
Ops Center, active/active
Ops Center is my own operations platform: inventory, patching, monitoring, alerting and approval-gated AI agents. It runs on both Docker hosts at once:
host #1 host #2 witness VM
┌──────────────┐ ┌──────────────┐ ┌───────────┐
│ api/frontend │ │ api/frontend │ │ │
│ workers │ │ workers │ │ │
│ postgres HA ─┼── repl ──┼─ postgres HA │ │ │
│ etcd ────────┼──────────┼─ etcd ───────┼────────┼─ etcd │
│ redis+sentinel ─────────┼ redis+sentinel ───────┼─ sentinel │
└──────────────┘ └──────────────┘ └───────────┘Postgres runs as an HA pair with etcd for leader election, Redis runs with Sentinel, and the small witness VM provides the third vote for both. Either Docker host can be patched and rebooted without the platform going down. The monitoring stack (Prometheus, Grafana, Loki, Alertmanager, cAdvisor, blackbox exporter) ships with it.
Rules I ended up with
- Pin versions for anything with a schema. Auto-updating Jellyfin: fine. Auto-updating a database or Home Assistant: planned, not automatic.
- Data volumes, not the root disk. The root filesystem only holds the OS and images. When it fills up, it's image cleanup, not data loss.
- One reverse proxy, real certificates. Internal HTTPS with valid certs removes a whole class of "just click through the warning" habits.
- Two hosts, split by failure domain. Things that should survive a host reboot run on both, or have a twin on the other node.
# EOF