jemonjam
engineer + builder + leader
A tour of my homelab
June 15, 2024

Last updated 7/5/2026

I’ve always enjoyed giving a bit of whimsy to my computer names. Recently, I’ve settled on naming them after characters from Patrick Rothfuss’ Kingkiller Chronicles Every machine in my house is named after a character from Patrick Rothfuss’s Kingkiller Chronicle. My main compute node is is elodin, the NAS is alder, the media box is felurian, and the lowly Raspberry Pi that was the original homelab node is kvothe.

The full rack. I'll fill it one day!

The guts of the lab are a 2-node Proxmox cluster with a raspberry pi running as a QDevice to maintain quorum. Elodin is the beefiest box running:

Most of the compute runs on elodin as a mix of VMs and LXC containers. My current setup:

GitOps & Backup

Important config, ansible playbooks, home-assistant definitions and more, are all stored in a monorepo. This also includes portainer stack definitions. This allows me to track changes across the fleet and easily roll them back (either from git or from backup). Not everything important is GitOps’ed; some things (for example, traefik routing) are still local to that node.

Backup is done in 4 passes:

  1. All VMs/LXCs have their root disk backed up to PBS running on elodin, in a redundant ZFS Pool
  2. Important proxmox config is written directly to git via ansible, to support GitOps
  3. The NAS is backed up via restic, encrypted in the cloud
  4. Proxmox itself is backed up to PBS

Network Layer

                    Internet
                       |
                 Cloudflare (tunnel + Access)
                       |
        UniFi  ─── Traefik (*.services.home, TLS)
          |              |
     ┌────┴────┬─────────┼───────────┬─────────┐
   Elodin    Alder   Felurian      Kvothe     Bast
  (Proxmox)  (NAS)   (Emby)     (Home Asst.) (gaming VM)

The network runs on top of a Ubiquiti Dream Machine. Internally, nodes are given a .home URL (for example, elodin.home). All traffic to *.services.home is routed to Traefik by the router; traefik then routes these to the appropriate internal service. Separately, Cloudflare DNS router *.internal.336.rocks to Traefik’s private IP (192.168.1.100). This, along with Let’s Encrypt certs set up at the Traefik level allow for https access to internal services through either domain.

A tailscale node exports the full subnet, which allows for VPN access to the entire network.

Cloudflare Tunnels are used for giving limited external access to 3rd parties. These are punched through via a cloudflared LXC.

http:
  routers:
    today_router:
      rule: "Host(`today.services.home`) || Host(`today.internal.336.rocks`)"
      service: today
      entryPoints: ["websecure"]
      tls: {}

  services:
    today:
      loadBalancer:
        servers:
          - url: "http://docker.home:8787"
        serversTransport: insecure
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: cloudflare
        domains:
          - main: "internal.336.rocks"
            sans:
              - "*.internal.336.rocks"

providers:
  file:
    directory: "/etc/traefik/conf"
    watch: true

certificatesResolvers:
  cloudflare:
    acme:
      email: myemail@gmail.com
      storage: /etc/traefik/acme.json
      caServer: https://acme-v02.api.letsencrypt.org/directory # prod (default)
      # caServer: https://acme-staging-v02.api.letsencrypt.org/directory # staging
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"
        delayBeforeCheck: 300

log:
  level: INFO

Smart Home

Home Assistant runs on a raspberry pi. All automations and scripts are run via on top of appdaemon (instead of defined in yaml), which makes testing and reasoning about the system much simpler.