Corvidae Cluster Homelab

  • #homelab
  • #self-hosting
  • #devops

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.