# Offline & Remote Access Guide Last updated: 2026-03-20 ## How DNS Resolution Works The homelab uses **split-horizon DNS** so services are reachable from anywhere — LAN, Tailscale VPN, or the open internet — using the same `*.vish.gg` domain names. ### Three Access Paths ``` ┌──────────────────────────────────────────────────────────────────────┐ │ DNS Query: nb.vish.gg │ ├──────────────┬──────────────────┬────────────────────────────────────┤ │ LAN Client │ Tailscale Client│ Internet Client │ │ (at home) │ (travel laptop) │ (phone on cellular) │ ├──────────────┼──────────────────┼────────────────────────────────────┤ │ DNS: AdGuard│ DNS: Headscale │ DNS: Cloudflare │ │ (192.168.0 │ MagicDNS → │ (1.1.1.1) │ │ .250) │ AdGuard │ │ ├──────────────┼──────────────────┼────────────────────────────────────┤ │ Resolves to:│ Resolves to: │ Resolves to: │ │ 100.85.21.51│ 100.85.21.51 │ 104.21.73.214 (Cloudflare) │ │ (NPM via TS)│ (NPM via TS) │ │ ├──────────────┼──────────────────┼────────────────────────────────────┤ │ Path: │ Path: │ Path: │ │ Client → │ Client → │ Client → Cloudflare → │ │ NPM (direct)│ Tailscale → │ Router → NPM → │ │ → backend │ NPM → backend │ backend │ ├──────────────┼──────────────────┼────────────────────────────────────┤ │ Latency: │ Latency: │ Latency: │ │ ~1ms │ ~5-50ms │ ~50-100ms │ │ (LAN) │ (Tailscale) │ (Cloudflare roundtrip) │ ├──────────────┼──────────────────┼────────────────────────────────────┤ │ Internet │ Internet │ Internet │ │ required? │ required? │ required? │ │ NO │ NO (peer-to-peer│ YES │ │ │ if both on TS) │ │ └──────────────┴──────────────────┴────────────────────────────────────┘ ``` ### Key: Everything Resolves to 100.85.21.51 All `*.vish.gg`, `*.thevish.io`, and `*.crista.love` domains resolve to `100.85.21.51` (matrix-ubuntu's Tailscale IP) when queried through AdGuard. This is NPM's address on the Tailscale network, reachable from: - **LAN clients** — via the router's DHCP DNS (AdGuard at 192.168.0.250) - **Remote Tailscale clients** — via Headscale MagicDNS which forwards to AdGuard - **Both paths hit NPM on its Tailscale IP**, which works from anywhere on the tailnet ## When Internet Goes Down If your WAN link drops: | What works | How | |------------|-----| | All `*.vish.gg` services | AdGuard returns Tailscale IP, NPM proxies locally | | MagicDNS names (`atlantis.tail.vish.gg`) | Headscale resolves directly | | Direct Tailscale IPs (100.x.x.x) | Always work between peers | | Olares/K8s (k9s, kubectl) | LAN access at 192.168.0.145 | | What breaks | Why | |-------------|-----| | External access (from internet) | Cloudflare can't reach you | | Cloudflare-only domains without split-horizon rewrite | DNS returns unreachable CF proxy IP | | Renovate, DDNS updates | Need internet to reach APIs | | DERP relays for remote peers | Remote Tailscale clients may lose connectivity | ## Access from Travel Laptop Your travel laptop (MSI Prestige) connects via Headscale VPN: 1. **Join the tailnet**: `tailscale up --login-server=https://headscale.vish.gg` 2. **DNS is automatic**: Headscale pushes AdGuard as the DNS server via MagicDNS 3. **All domains work**: `nb.vish.gg`, `git.vish.gg`, etc. resolve to NPM's Tailscale IP 4. **No VPN split tunneling needed**: Only homelab traffic routes through Tailscale ```bash # From the travel laptop: curl https://nb.vish.gg/ # → 100.85.21.51 (Tailscale) → NPM → backend curl https://gf.vish.gg/ # → 100.85.21.51 (Tailscale) → NPM → Grafana ssh homelab.tail.vish.gg # → MagicDNS → direct Tailscale peer ``` ### If Headscale Is Down If the Headscale control server (calypso) is unreachable, already-connected peers maintain their connections. New peers can't join. Use direct Tailscale IPs as fallback: | Service | Direct URL | |---------|-----------| | Grafana | `http://100.67.40.126:3300` | | NetBox | `http://100.67.40.126:8443` | | Portainer | `https://100.83.230.112:9443` | | Gitea | `http://100.103.48.78:3052` | ## MagicDNS (.tail.vish.gg) Headscale MagicDNS provides `.tail.vish.gg` for all peers: | Hostname | Tailscale IP | Use | |----------|-------------|-----| | atlantis.tail.vish.gg | 100.83.230.112 | NAS, media | | calypso.tail.vish.gg | 100.103.48.78 | NAS, Gitea, auth | | homelab.tail.vish.gg | 100.67.40.126 | Monitoring, tools | | matrix-ubuntu.tail.vish.gg | 100.85.21.51 | NPM, Matrix, Mastodon | | pve.tail.vish.gg | 100.87.12.28 | Proxmox | | pi-5.tail.vish.gg | 100.77.151.40 | Uptime Kuma | | vish-concord-nuc.tail.vish.gg | 100.72.55.21 | Home Assistant, edge | | setillo.tail.vish.gg | 100.125.0.20 | Remote NAS | | seattle.tail.vish.gg | 100.82.197.124 | Cloud VPS | | truenas-scale.tail.vish.gg | 100.75.252.64 | TrueNAS | **Note:** MagicDNS only works on devices connected to the Headscale tailnet. It requires the Tailscale client to override the system DNS resolver. Some devices (like pi-5 with AdGuard as DNS) can't resolve `.tail.vish.gg` names — use Tailscale IPs directly instead. ## AdGuard DNS Rewrites (Split-Horizon) AdGuard on Calypso (`192.168.0.250:9080`) has wildcard rewrites: | Domain Pattern | Resolves To | Purpose | |---------------|-------------|---------| | `*.vish.gg` | `100.85.21.51` | NPM via Tailscale | | `*.thevish.io` | `100.85.21.51` | NPM via Tailscale | | `*.crista.love` | `100.85.21.51` | NPM via Tailscale | | `derp.vish.gg` | `192.168.0.250` | DERP relay (direct) | | `derp-atl.vish.gg` | `192.168.0.200` | DERP relay (direct) | | `turn.thevish.io` | `192.168.0.200` | TURN server (direct) | ## SSL Certificates All services use **Let's Encrypt wildcard certificates** (issued via DNS challenge with Cloudflare API): | Certificate | Domains | Issuer | |------------|---------|--------| | Cert 8 | `*.vish.gg`, `vish.gg` | ZeroSSL (via acme.sh) | | Cert 9 | `*.thevish.io`, `thevish.io` | ZeroSSL (via acme.sh) | | Cert 10 | `*.crista.love`, `crista.love` | ZeroSSL (via acme.sh) | These certs are **publicly trusted** — no certificate warnings on any access path (LAN, Tailscale, or internet). ### Certificate Renewal acme.sh is installed on matrix-ubuntu (`/home/test/.acme.sh/`) with auto-renewal via cron. To manually renew: ```bash ssh matrix-ubuntu export CF_Token="REDACTED_TOKEN" # pragma: allowlist secret ~/.acme.sh/acme.sh --renew -d '*.vish.gg' -d 'vish.gg' --force ~/.acme.sh/acme.sh --renew -d '*.thevish.io' -d 'thevish.io' --force ~/.acme.sh/acme.sh --renew -d '*.crista.love' -d 'crista.love' --force # Then re-upload to NPM (certs need to be uploaded via NPM API or UI) ``` ## Quick Reference ### I'm at home on WiFi Just use `https://nb.vish.gg` — AdGuard resolves to NPM's Tailscale IP, works instantly. ### I'm traveling with the laptop Connect to Headscale tailnet → same URLs work: `https://nb.vish.gg` ### I'm on my phone (no VPN) Use the public URLs: `https://nb.vish.gg` → goes through Cloudflare as normal. ### Internet is down at home All services still work from LAN via AdGuard → Tailscale IP → NPM. No Cloudflare dependency. ### I need to access a service directly (no NPM) Use Tailscale IPs: `http://100.67.40.126:3300` (Grafana), `http://100.83.230.112:9443` (Portainer) ### Everything is down — emergency access SSH via Tailscale: `ssh homelab` (uses ~/.ssh/config with Tailscale IPs) ## Related Documentation - [Split-Horizon DNS Implementation](split-horizon-dns.md) - [NPM Migration Plan](npm-migration-to-matrix-ubuntu.md) - [Authentik SSO](authentik-sso.md) - [Image Update Guide](../admin/IMAGE_UPDATE_GUIDE.md)