Skip to content

Homelab

Notes on my homelab setup. Almost everything is at one main site, on two Proxmox hosts behind an OPNsense router. A travel router at a second location joins it over Tailscale.

  • Proxmox: the hypervisor, VMs vs LXCs, GPU and disk passthrough.
  • Network: OPNsense, AdGuard Home DNS synced across sites, UniFi, and a GL.iNet travel router.
  • Docker stack: about a hundred self-hosted services on one host, behind Traefik and CrowdSec.
  • Services & monitoring: what runs and how I know it's up.
  • Notifications: everything ends up as a push from a self-hosted ntfy.
  • Backups, file sync and SMB shares are in their own section: Files.

Overview

flowchart LR
    subgraph main["Main site"]
        router["OPNsense router<br/>firewall, DNS, Tailscale exit node"]
        subgraph pve1["Proxmox host 1: services"]
            docker["Docker host VM<br/>GPU, ~100 containers"]
            work["Workstation VM"]
            nas["NAS VM<br/>OpenMediaVault"]
            unifi["UniFi controller LXC"]
        end
        subgraph pve2["Proxmox host 2: backups"]
            bnas["Backup NAS VM<br/>OpenMediaVault"]
            pbs["Proxmox Backup Server LXC"]
            dns2["AdGuard + Unbound LXCs"]
        end
        router --- pve1
        router --- pve2
    end
    subgraph studio["Studio"]
        travel["GL.iNet travel router"]
    end
    laptop["Laptop / phone"]
    docker -- "SMB shares" --> nas
    pve1 -- "restic / vzdump" --> pve2
    main <-. tailnet .-> studio
    laptop <-. tailnet .-> main
Machine Runs Role
Router OPNsense Firewall, router and DNS for the main site, and a Tailscale exit node. See Network.
Proxmox host 1 Proxmox VE The services: the Docker host, the workstation, the NAS and the UniFi controller. See Proxmox.
Docker host Ubuntu Server VM with an NVIDIA GPU passed through Runs the whole Docker stack. The GPU is used for Immich ML, Frigate, Whisper and Ollama.
Workstation Ubuntu VM Remote desktop (xrdp) and SSH for heavier work, such as running Claude Code over Herdr.
NAS OpenMediaVault VM The primary copy of photos, files and the family library. Shared over SMB and synced to devices with Syncthing.
Proxmox host 2 Proxmox VE Separate hardware just for backups: a backup NAS VM and Proxmox Backup Server. It also runs a second AdGuard Home and Unbound, so DNS survives the router being down.
Travel router GL.iNet Router at the studio, also on the tailnet.

Why VMs under Proxmox

Each role being its own VM means I can snapshot the Docker host before a risky upgrade, rebuild the NAS without touching anything else, and move things between disks. The cost is a little overhead and passing the GPU through to one VM.

Tailscale

Link: tailscale.com

All my machines are on one Tailscale network (a "tailnet"). Every machine gets a stable private IP and a MagicDNS name, and they can reach each other from anywhere without opening ports on the router or setting up a VPN server.

In practice this means ~/.ssh/config entries simply point at the Tailscale address, and SSH, Herdr's remote machines and everything else work the same at home or away:

~/.ssh/config
Host <computer_name>
    HostName <tailscale_ip_or_magicdns_name>

Things worth setting up:

  • Tags for servers. Servers are registered with a tag rather than my user account, so their node keys don't expire with my login, and ACLs can target all servers at once.
  • Exit node. The OPNsense router advertises itself as an exit node. On a phone or laptop, tailscale set --exit-node=<router> sends all traffic through it.
  • Routers on the tailnet. With the routers themselves on the tailnet, both the main site and the studio stay reachable, and a router can advertise its LAN as a subnet route for devices that can't run Tailscale.
  • The reverse proxy over Tailscale. Traefik on the Docker host also routes a few services that live on other machines, reaching them by their Tailscale address. See Docker stack.