Files
felhom.eu/documentation/operations/tailscale.md
T
admin 83c47eaf23 docs(ops): Tailscale makes the N100 (felhom-pve) location-independent
felhom-pve joins the tailnet as a host package (100.70.170.35, expiry
disabled); DooPlex already advertised 192.168.0.0/24 via its GitOps k3s
tailscale pod (100.107.87.53). ssh felhom-pve now targets the tailnet IP
(direct-over-LAN at home, tunnel when away); felhom-pve-lan = LAN fallback.
--accept-dns=false on the host. Host converted static->DHCP (reservation
keeps .162 at home). Measured: --accept-routes hairpins the local subnet
at home -> travel-only opt-in. PBS is offsite (own WireGuard tunnel), needs
none of this. Recorded not fixed: controller<->agent plane hard-pinned to
192.168.0.162 (agent listen_addr + guest bootstrap.json) -> not yet portable
off-LAN. No code changed. Full doc: documentation/operations/tailscale.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XMForrE4c1wZxd9LukxYVt
2026-07-19 13:31:09 +02:00

6.6 KiB

Tailscale — location-independent access to felhom-pve (the N100)

Added 2026-07-19. Goal: the demo Proxmox host (N100, felhom-pve) can be taken to a different LAN (e.g. a vacation home) and stay reachable from DooPlex, while its own dependencies keep working. The ssh felhom-pve alias remains the interface — only its transport changed.

Topology (who is what)

Node Tailnet name Tailnet IPv4 Transport it provides Managed as
DooPlex (192.168.0.180) dooplex 100.107.87.53 subnet router advertising 192.168.0.0/24; expiry disabled k3s pod admin-system/tailscale (hostNetwork, GitOps/ArgoCD) — 157 d old, predates this task
N100 (192.168.0.162) felhom-pve 100.70.170.35 its own node; expiry disabled host package tailscale (Debian trixie apt repo), tailscaled.service

Tailnet: nagyfenyvesi.viktor@gmail.com (Tailscale Free). Both nodes run v1.98.x.

ssh felhom-pve now resolves to 100.70.170.35 (see ~/.ssh/config on DooPlex). Tailscale takes the direct LAN path when both boxes are home (measured: pong … via 192.168.0.162:41641 in 1ms) and falls back to the tunnel / a DERP relay when the N100 is remote. felhom-pve-lan192.168.0.162 is the explicit LAN-only fallback.

Hard rule: --accept-dns=false on the host node

The N100's tailscaled runs with --accept-dns=false (CorpDNS:false). MagicDNS must never rewrite /etc/resolv.conf: the box relies on pi-hole (192.168.0.250) for split-horizon (gitea.dooplex.hu192.168.0.180). Verified byte-identical after tailscale up. (DooPlex's containerised node runs TS_ACCEPT_DNS=true, but because it is a pod with its own mount namespace that only rewrites the container's resolv.conf — the DooPlex host resolv.conf is untouched.)

The accept-routes spike (measured 2026-07-19) — leave it OFF at home

--accept-routes is OFF on felhom-pve and should stay off while the box is on 192.168.0.0/24. Enabling it there was tested and degraded local traffic:

  • Tailscale installed 192.168.0.0/24 dev tailscale0 into routing table 52 with an ip rule 5270: from all lookup 52 that outranks main, so the whole local subnet hijacked onto the tunnel: ip route get 192.168.0.180 and …0.250 flipped from vmbr0 to tailscale0, ping RTT to .180 went 0.3 ms → ~68 ms, and inbound SSH went asymmetric and stalled.
  • PBS was unaffected (10.77.0.1 stays on wg-felhom), because PBS does not use this subnet.
  • Rollback (tailscale set --accept-routes=false) restored direct vmbr0 immediately.

When travelling, the N100 is on a different subnet, so accepting 192.168.0.0/24 no longer overlaps the local net and is safe — it lets the box reach 192.168.0.180/gitea over the tunnel. Treat it as an opt-in, travel-only toggle you turn on only if you need a home-LAN service (e.g. a new controller-image pull), and turn back OFF before the box returns home. Management SSH over 100.x is immune to the hairpin either way (that path was used to perform the rollback).

PBS does NOT depend on the home LAN

Contrary to the original assumption, PBS is offsite: storage.cfg targets 10.77.0.1 over the WireGuard interface wg-felhom whose endpoint is a public Hetzner address (…:443). It works from any location with internet and needs neither the subnet router nor --accept-routes. This is why the subnet router is nice-to-have (gitea image pulls while away), not load-bearing.

Key expiry

Disabled on both nodes in the admin console (a node whose key expires mid-trip drops off the tailnet). Confirm on the Machines page: each row shows an Expiry disabled badge.

Rollback / uninstall (host node)

ssh felhom-pve "tailscale set --accept-routes=false"     # undo the travel toggle
ssh felhom-pve "tailscale down"                           # leave the tailnet (reversible: tailscale up)
ssh felhom-pve "systemctl disable --now tailscaled && apt-get purge -y tailscale"   # full removal
# then repoint ~/.ssh/config Host felhom-pve back to 192.168.0.162 (or use felhom-pve-lan)

Portability of the N100 itself

  • Host network is DHCP (converted 2026-07-19; iface vmbr0 inet dhcp). At home a DHCP reservation pins MAC 68:1d:ef:5d:a6:64192.168.0.162 so home behaviour is unchanged. Revert on the box: cp /root/interfaces.static-revert-20260719 /etc/network/interfaces && ifreload -a.
  • Guest 9201 is already DHCP.

⚠️ Known blocker — the controller↔agent plane is pinned to 192.168.0.162 (NOT fixed)

felhom-agent binds listen_addr: 192.168.0.162:8443 (in /etc/felhom-agent/agent.json) and guest 9201's /etc/felhom-bootstrap/bootstrap.json points its controller at 192.168.0.162:8443. On a foreign LAN the host will not have 192.168.0.162, so the agent cannot bind it (agent down) and the guest cannot reach it. Tailscale and host-DHCP do not fix this (guests bridge at L2). Making the box fully functional off-LAN needs a design change — bind 0.0.0.0:8443 and give the guest a stable way to reach the host (e.g. a host-only bridge with a fixed private IP). Until then the N100 is reachable and backs up off-LAN, but its local operator plane is not portable.

Off-LAN DNS note

At a foreign LAN, DHCP repoints the host's resolv.conf at the local resolver, so gitea.dooplex.hu (pi-hole split-horizon) will not resolve. To pull a controller image while away, either turn on the travel --accept-routes toggle and add a temporary 192.168.0.180 gitea.dooplex.hu line to /etc/hosts, or point DNS at 192.168.0.250 over the subnet route. External customer access is via Cloudflare Tunnel and is unaffected.

Vacation-day checklist (for the operator)

  1. At the destination, connect the N100 by wired ethernet, power on, wait ~2 min.
  2. From DooPlex: tailscale-side — check the pod: sudo kubectl -n admin-system exec deploy/tailscale -- tailscale status shows felhom-pve online (likely relayed/DERP at first — fine).
  3. ssh felhom-pve pveversion — management reachable over the tailnet.
  4. ssh felhom-pve "pvesm status | grep -i pbs"the real off-LAN PBS test (could not be validated at home; PBS rides its own WireGuard tunnel, so it should be green).
  5. Demo URL via Cloudflare Tunnel loads (customer-facing path, independent of all the above).
  6. If you need a home-LAN service (gitea image pull): ssh felhom-pve "tailscale set --accept-routes=true"
    • /etc/hosts entry as above — then set it back to false before the box comes home.
  7. Expect the controller↔agent plane to be down off-LAN until the pinned-.162 blocker above is fixed.