Corvidae Cluster Homelab
There’s a particular kind of satisfaction that comes from hosting your own stuff. Running services on hardware you own, on your own terms, that you can poke and break and fix. That’s the appeal of a homelab, at least it was for me when I started building one a while back, mostly to get better at DevOps and infrastructure.
The hardware is two small machines: a Dell Optiplex 7070 Micro and a GMKtec M5 Plus, both running Proxmox. If you are not already aware of this wonderful invention, is an easy-to-setup and easy-to-manage hypervisor that lets you spin up virtual machines and containers on bare metal. The two boxes are clustered together, with a Raspberry Pi 5 acting as a tiebreaker for quorum. That last part sounds fancier than it is. A two-node cluster has a fundamental problem: if neither node can reach the other, they can’t agree on who’s in charge. The Pi doesn’t run any workloads. It just casts a deciding vote.
Most of the Docker services live on a VM called sylvanus, managed through Komodo. Here’s a map of how everything fits together, then a rundown of each service.
The map
Arrows show the critical path: Pi-hole resolves .lan names, Traefik routes the traffic, step-ca issues the TLS certificates. The Docker services on corax read media from corvus over NFS — that’s the cross-node storage edge that cost me a slow switch.
flowchart LR
subgraph cluster["🪶 corvidae cluster (Proxmox VE)"]
direction LR
subgraph corvus["🖥️ corvus — Dell Optiplex 7070"]
direction LR
subgraph net["🌐 Network"]
direction TB
pihole["🌐 Pi-hole\nDNS + Ad Blocking"]
stepca["🔒 step-ca\nInternal CA"]
end
traefik["🔀 Traefik\nReverse Proxy"]
subgraph lxcs["📦 LXC Containers"]
direction TB
jellyfin["🎞️ Jellyfin\nMedia"]
forgejo["🏗️ Forgejo\nGit"]
kuma["📈 Uptime Kuma\nMonitoring"]
glance["📋 Glance\nDashboard"]
runner["⚙️ Forgejo Runner\nActions CI"]
pbs["💾 PBS\nBackups"]
timemachine["🕰️ Time Machine\nMac Backup"]
wikibot["📓 Wikibot\nClaude Code Bot"]
hermes["⚡ Hermes\nAI Coding Agent"]
end
wd["🗄️ WD 10TB\nMedia + Backups"]
end
subgraph corax["🖥️ corax — GMKtec M5 Plus"]
subgraph sylvanus["🐳 sylvanus (Docker VM)"]
direction TB
komodo["🦎 Komodo\nDeploy"]
seafile["☁️ Seafile\nFile Sync"]
vaultwarden["🔑 Vaultwarden\nPasswords"]
n8n["🤖 n8n\nAutomation"]
qdrant["🔎 Qdrant\nVector DB"]
immich["🖼️ Immich\nPhotos"]
subgraph arr["🎬 Media Stack"]
direction LR
gluetun["🛡️ Gluetun\nVPN (Mullvad)"]
qbit["📥 qBittorrent"]
prowlarr["🔍 Prowlarr"]
radarr["🎬 Radarr"]
sonarr["📺 Sonarr"]
jellyseerr["🍿 Jellyseerr"]
end
end
end
end
magpi["🥧 magpi — Raspberry Pi 5\nQDevice tiebreaker"]
pihole -->|"DNS (.lan)"| traefik
stepca -->|"TLS certs"| traefik
traefik ---> komodo
traefik ---> jellyfin
runner -.->|"CI jobs"| forgejo
qbit --> gluetun
n8n -.->|"Embeddings"| qdrant
wd -.->|"NFS"| sylvanus
wd -.->|"Media"| jellyfin
wd -.->|"Datastore"| pbs
wd -.-> timemachine
Here’s what’s running and why.
Komodo
Komodo is what ties the Docker side of things together. It manages my containers and their compose stacks from one place, pulls config from a git repo, and gives me a view of what’s deployed where. I started out with Portainer and moved to Komodo because I wanted my stacks defined in version control rather than clicked together in a UI.
Traefik
Traefik is the reverse proxy that sits in front of everything. It routes incoming requests to the right service based on hostname and handles TLS termination, picking up new services automatically from labels rather than hand-written config files. It replaced Nginx Proxy Manager, which I’d started with — the move was mostly about wanting routing defined alongside the services themselves.
step-ca
step-ca is a small internal certificate authority. It issues the TLS certificates that Traefik serves, so internal services get real HTTPS without me reaching for public certificates or clicking through browser warnings. Everything inside the network trusts the one CA.
Pi-hole
Pi-hole handles DNS for the whole network and blocks ads and trackers at the resolver level. Because it’s the DNS server, it also gives me local hostnames for my services, which is what makes the internal TLS setup pleasant to use.
Jellyfin
Jellyfin is the media server — movies, shows, music, streamed to whatever device I’m on. It’s the open-source, no-subscription answer to Plex, and it reads its library off an NFS share (the one that taught me the networking lesson below).
The *arr stack
The *arr stack — Sonarr, Radarr, and friends — automates the boring parts of managing a media library: tracking what I want, grabbing it, and filing it into the folders Jellyfin reads from. It’s a handful of cooperating services rather than one app, which is half the reason it’s fun to run.
Immich
Immich is self-hosted photo and video backup, the kind of thing you’d otherwise hand to Google Photos. Phones back up to it automatically, and it does the timeline, albums, and face/object search locally instead of in someone else’s cloud.
Seafile
Seafile is file sync and sharing — my self-hosted Dropbox. I replaced Nextcloud with it because I mostly wanted fast, reliable file sync and Seafile does that one job well without the surrounding sprawl.
Forgejo
Forgejo is self-hosted git, a lightweight forge for my own repositories. It’s where the config that Komodo and Ansible pull from lives, so the lab is, to a degree, hosting the source of its own configuration.
Proxmox Backup Server
Proxmox Backup Server runs the nightly backups. It does deduplicated, incremental backups of the VMs and containers, so a broken experiment is a restore away rather than a rebuild. Given how much of this lab exists so I can break things on purpose, that safety net matters.
Three tools I started with aren’t running anymore: Nginx Proxy Manager, Portainer, and Nextcloud. I replaced them with Traefik, Komodo, and Seafile. The originals aren’t bad tools. You just outgrow your first choices in a homelab. That’s kind of the point.
One thing that tripped me up was when I moved my Docker VM to the second physical node, the NFS share it uses for media suddenly had to cross the network. Things got slower. Turned out the switch between the machines was 100 Mbps (since I was using the in-built switch that my router has). I replaced it with a gigabit one and the problem went away. So keep that in mind… you will need a gigabit switch if you plan to have more than one node.
The thing that’s had the most lasting impact is Ansible. Writing playbooks to provision each guest from scratch means I can rebuild anything quickly and actually understand what I’ve set up. Previously I’d configure something, not write it down, and then later have no idea how it worked. Ansible fixed that. Documentation as code, sort of.
Not to mention, I also have actual documentation too, and I make sure to keep it updated. I host it as a wiki using MkDocs, and have a dedicated wikibot LXC that I can interact with.
I’ll write more about specific parts of this as I go.