Files
homelab-optimized/docs/infrastructure/offline-and-remote-access.md
Gitea Mirror Bot 3cb5034cc6
Some checks failed
Documentation / Build Docusaurus (push) Failing after 18m5s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-03-20 09:49:40 UTC
2026-03-20 09:49:40 +00:00

9.3 KiB

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
# 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 <hostname>.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:

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)