Vps Pacman

vps-pacman Host Configuration

Role: Edge VPS (L1 gateway) — public SSL terminator and reverse proxy
Platform: x86_64-linux (Vultr VPS, QEMU guest)

Architecture Role

vps-pacman is the L1 gateway in our two-layer architecture. It is the only host with a public IP. It terminates SSL, handles ACME wildcard certificate issuance, runs OAuth2-Proxy for public SSO, and reverse-proxies all traffic to lab-matrix over Tailscale.

Services

CategoryServicePurpose
WebNginxPublic SSL terminator (QUIC/kTLS), reverse proxy to L2
CertsACMEWildcard *.alienzj.org via Cloudflare DNS-01
SSOOAuth2-ProxyPublic SSO gateway (auth.alienzj.org)
ProxySing-boxMulti-port egress proxy
RelaySyncthing relayPublic syncthing relay server
FirewallFail2banLog-based intrusion prevention

Imported Modules

  • modules/modules.nix: Server role, network profiles (jp/vps, ts0), SSH hardening, sing-box
  • modules/storage.nix: VPS block storage with disko
  • modules/nginx.nix: L1→L2 proxy map (homelabServices) — auto-generates vhosts for all public services, proxying to lab-matrix over Tailscale MagicDNS

Nginx Proxy Map

The L1→L2 mapping is defined declaratively in hosts/vps-pacman/modules/nginx.nix:

homelabServices = {
    id    = "lab-matrix";   # Kanidm
    atuin = "lab-matrix";   # Atuin shell history
    mail  = "lab-matrix";   # Stalwart
    git   = "lab-matrix";   # Forgejo
    rss   = "lab-matrix";   # FreshRSS
    bw    = "lab-matrix";   # Vaultwarden
    books = "lab-matrix";   # Calibre
    immich = "lab-matrix";  # Immich
    links  = "lab-matrix";  # Linkwarden
    pdf    = "lab-matrix";  # Stirling-PDF
    affine = "lab-matrix";  # Affine
    ollama  = "id3-eniac";  # Ollama (separate host)
    jupyter = "id3-eniac";  # Jupyter (separate host)
};

Each entry auto-generates a vhost at <name>.alienzj.org that proxies to the target host over Tailscale. Services can run on any L2 host in the tailnet — lab-matrix for most services, id3-eniac for GPU-accelerated workloads (Ollama, JupyterHub). Adding a new service on any host is a one-line change to this map.

Tailscale DNS Resolution

Backend hostnames are resolved via the Tailscale MagicDNS resolver. There are two modes, controlled by the modules.services.web.nginx.homelabProxy.tailnetSuffix option in the host config:

ModeConfigHow it works
MagicDNS FQDN (default for servers)tailnetSuffix = "tailf0ad7b.ts.net"Resolves ${host}.${tailnetSuffix}. Works when --accept-dns is disabled on the node (common on servers to avoid Tailscale taking over system DNS).
Short hostnametailnetSuffix = nullResolves bare ${host}. Requires tailscale set --accept-dns=true on the node so the resolver auto-appends the MagicDNS search domain.

Find your tailnet suffix with tailscale dns status on any node in the tailnet.

Traffic between L1 and L2 is plain HTTP inside the Tailscale WireGuard tunnel — TLS is terminated at L1 and not re-established to L2. The WireGuard link provides ChaCha20-Poly1305 encryption, so double TLS is unnecessary.

Setup

See Homelab Bootstrap Guide for the step-by-step operational setup — SSH key provisioning, secrets preparation, OAuth2-Proxy env file, and ACME certificate issuance.