wiki-vault/Sources/Homelab/traefik-deployment.md
Travis Herbranson 34a268d8dc migration: copy 62 notes from pbs-projects and homelab-projects
Two-layer structure: Sources (raw notes) + Wiki (compile output)
Four domains: Dev (40), Venture (3), Homelab (23), Reference (0)
Includes CLAUDE.md spec, index pages at all levels, compile log

Co-Authored-By: Lovebug <lovebug@herbylab.dev>
2026-05-08 17:21:13 -04:00

6.9 KiB

created path project status tags type updated
2026-05-06 Sources/Homelab traefik-deployment active
homelab
traefik
dns
knot
tls
cloudflare
tailscale
mcp
project-plan 2026-05-06

Traefik Deployment — Homelab Ingress

Goal

Replace ad-hoc host:port access with consistent named endpoints for ~8 services across three surfaces (public, private, east-west). Build the full ingress stack — Traefik, Knot DNS, wildcard TLS — and migrate MCP behind it as the first real service.

Locked Architecture

Topology

  • Traefik VM on PVE (own VM, runs Docker + tailscaled + cloudflared)
  • Knot primary in LXC on PVE
  • Knot secondary in Docker on NAS (Ubuntu bare metal)
  • Pi-hole + Unbound stays as recursive frontend + ad-blocker
  • Cross-VLAN firewall locks PVE↔NAS to explicit flows (Knot AXFR, Traefik→NAS services)

DNS

  • Knot authoritative for herbylab.dev internal
  • Active/passive replication via native AXFR + NOTIFY
  • Pi-hole conditional forwards herbylab.dev queries to Knot
  • Split-horizon: same name resolves to Traefik VM internally, Cloudflare externally
  • Zone file in git, Ansible deploys to primary

TLS

  • Wildcard *.herbylab.dev via Traefik + Cloudflare DNS-01
  • One cert covers public and private surfaces
  • Tailscale certs dropped from the plan

Naming

  • Capability-based: .herbylab.dev regardless of host
  • No lab. infix, no dev. prefix — domain is by function, not location

Routing surfaces

  • Public: Cloudflare → cloudflared (on Traefik VM) → Traefik → service
  • Private: Tailscale → Traefik VM tailnet IP → service (DNS via Pi-hole → Knot)
  • East-west: direct, never through Traefik

Visual

                ┌─────────────────────┐
                │   Cloudflare Edge   │
                │  *.herbylab.dev     │
                └──────────┬──────────┘
                           │ tunnel

tailnet ────────────────────┼──────────────── tailscale0 │ ┌──────────▼──────────┐ │ Traefik VM (PVE) │ │ - traefik │ │ - cloudflared │ │ - tailscaled │ └──────────┬──────────┘ │ ┌────────────────┼────────────────┐ │ │ │ ┌───▼──┐ ┌───▼──┐ ┌───▼──┐ │ MCP │ │whoami│ │ ... │ └──────┘ └──────┘ └──────┘

Build Phases

Phase 1 — Foundations

No services routed yet. Each step produces a verifiable artifact.

  • 1.1 Cloudflare API token
    • Scope: herbylab.dev zone, DNS edit permissions only
    • Verify with curl against CF API
    • Store in password manager
  • 1.2 Traefik VM on PVE
    • Fresh Debian or Ubuntu, 1 vCPU, 1GB RAM, on PVE VLAN
    • Install Docker, Tailscale; tailscale up
    • Verify: pingable from laptop over tailnet
    • Snapshot
  • 1.3 Knot primary in LXC on PVE
    • Stand up LXC, install Knot
    • Load minimal zone file with one test record
    • Verify: dig test.herbylab.dev @ returns expected answer
  • 1.4 Knot secondary in Docker on NAS
    • Configure as AXFR slave
    • Open cross-VLAN firewall rule for AXFR
    • Verify: dig test.herbylab.dev @ matches primary
  • 1.5 Pi-hole conditional forwarding
    • Configure Pi-hole: herbylab.dev → Knot
    • Verify from a normal client: dig test.herbylab.dev returns Knot's answer
  • 1.6 Wildcard cert via Traefik + Cloudflare DNS-01
    • Minimal Traefik config, ACME with DNS-01 challenge
    • Request *.herbylab.dev
    • Verify: cert in acme.json, expiry ~90 days out
    • No services routed yet

Phase 1 checkpoint: working DNS chain, working wildcard cert, Traefik VM with no routes. Production unchanged. Safe abort point.

Phase 2 — Canary (whoami)

  • 2.1 Deploy whoami on Traefik VM
    • Docker compose, labels for both public and private surfaces
  • 2.2 Add DNS records
    • Knot zone: whoami.herbylab.dev → Traefik VM tailnet IP
    • Cloudflare: whoami.herbylab.dev → cloudflared tunnel
  • 2.3 Four-position verification
    • Laptop on tailnet → https://whoami.herbylab.dev works, real cert
    • Laptop off tailnet (LTE hotspot) → same URL works via cloudflared
    • dig from inside LAN returns Traefik VM IP
    • dig from outside returns Cloudflare IP
  • 2.4 Decision: keep whoami as permanent diagnostic, or tear down

Phase 3 — MCP Migration

Parallel-path safety net. Existing cloudflared → MCP stays running until the new path is verified.

  • 3.1 Add Traefik labels to MCP container
    • Add internal DNS record in Knot
    • Existing public path untouched
  • 3.2 Verify private surface first
    • From tailnet: https://mcp.herbylab.dev → Traefik → MCP works
  • 3.3 Cut public traffic over
    • Flip Cloudflare DNS / tunnel config to land mcp.herbylab.dev on Traefik
    • Verify Claude can still call MCP
    • Rollback path: flip DNS back
  • 3.4 Decommission old direct cloudflared route
    • After a few days of stability

Open Items (Build-Time Decisions)

  • Inventory current *.herbylab.dev records to migrate from Pi-hole local DNS into Knot zone file
  • TTL strategy — start at 300 during buildout, raise to 3600 once stable
  • Second Pi-hole instance for redundancy (parallel work, not blocking)
  • Pi-hole sync method — gravity-sync vs Teleporter
  • Ansible role structure for Knot primary deployment

Key Principles

  • Capability-based naming. Domain reflects function, not host. Service can move hosts without renaming.
  • Surfaces compose, they don't overlap. Cloudflare = public auth + ACME validation. Tailscale = device naming on tailnet. Traefik = service routing once a request lands. Internal DNS = telling LAN clients where to send the packet.
  • East-west stays direct. Service-to-service inside Docker/LAN never traverses Traefik.
  • Build outside-in for foundations, inside-out for services. Cert and DNS first; canary before production.
  • Parallel paths for migration. Never cut over working infrastructure without a rollback flip.

Reference

Prior session notes: Tech/Sessions/homelab-dns-decision.md (Knot architecture lock-in, alternatives considered, why split-horizon with public domain).

...sent from Jenny & Travis