chore: link vault wiki to Gitea
This commit is contained in:
145
02-selfhosting/dns-networking/tailscale-homelab-remote-access.md
Normal file
145
02-selfhosting/dns-networking/tailscale-homelab-remote-access.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: "Tailscale for Homelab Remote Access"
|
||||
domain: selfhosting
|
||||
category: dns-networking
|
||||
tags: [tailscale, vpn, remote-access, wireguard, homelab]
|
||||
status: published
|
||||
created: 2026-03-08
|
||||
updated: 2026-03-08
|
||||
---
|
||||
|
||||
# Tailscale for Homelab Remote Access
|
||||
|
||||
Tailscale is how I access my home services from anywhere. It creates a private encrypted mesh network between all your devices — no port forwarding, no dynamic DNS, no exposing anything to the internet. It just works, which is what you want from networking infrastructure.
|
||||
|
||||
## The Short Answer
|
||||
|
||||
Install Tailscale on every device you want connected. Sign in with the same account. Done — all devices can reach each other by hostname.
|
||||
|
||||
```bash
|
||||
# Install on Linux
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
sudo tailscale up
|
||||
|
||||
# Check status
|
||||
tailscale status
|
||||
|
||||
# See your devices and IPs
|
||||
tailscale status --json | jq '.Peer[] | {Name: .HostName, IP: .TailscaleIPs[0]}'
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
Tailscale sits on top of WireGuard — the modern, fast, audited VPN protocol. Each device gets a `100.x.x.x` address on your tailnet (private network). Traffic between devices is encrypted WireGuard tunnels, peer-to-peer when possible, routed through Tailscale's DERP relay servers when direct connection isn't possible (restrictive NAT, cellular, etc.).
|
||||
|
||||
The key thing: your home server's services never need to be exposed to the public internet. They stay bound to `localhost` or your LAN IP, and Tailscale makes them accessible from your other devices over the tailnet.
|
||||
|
||||
## MagicDNS
|
||||
|
||||
Enable MagicDNS in the Tailscale admin console (login.tailscale.com → DNS → Enable MagicDNS). This assigns each device a stable hostname based on its machine name.
|
||||
|
||||
```
|
||||
# Instead of remembering 100.64.x.x
|
||||
http://homelab:3000
|
||||
|
||||
# Or the full MagicDNS name (works from anywhere on the tailnet)
|
||||
http://homelab.tail-xxxxx.ts.net:3000
|
||||
```
|
||||
|
||||
No manual DNS configuration on any device. When Tailscale is running, hostnames resolve automatically.
|
||||
|
||||
## Installation by Platform
|
||||
|
||||
**Linux (server/desktop):**
|
||||
```bash
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
sudo tailscale up
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
Download from Mac App Store or tailscale.com/download/mac. Sign in through the menu bar app.
|
||||
|
||||
**iOS/iPadOS:**
|
||||
App Store → Tailscale. Sign in, enable the VPN.
|
||||
|
||||
**Windows:**
|
||||
Download installer from tailscale.com. Runs as a system service.
|
||||
|
||||
## Making Services Accessible Over Tailscale
|
||||
|
||||
By default, Docker services bind to `0.0.0.0` — they're already reachable on the Tailscale interface. Verify:
|
||||
|
||||
```bash
|
||||
docker ps --format "table {{.Names}}\t{{.Ports}}"
|
||||
```
|
||||
|
||||
Look for `0.0.0.0:PORT` in the output. If you see `127.0.0.1:PORT`, the service is bound to localhost only and won't be reachable. Fix it in the compose file by removing the `127.0.0.1:` prefix from the port mapping.
|
||||
|
||||
Ollama on Linux defaults to localhost. Override it:
|
||||
|
||||
```bash
|
||||
# Add to /etc/systemd/system/ollama.service.d/override.conf
|
||||
[Service]
|
||||
Environment="OLLAMA_HOST=0.0.0.0"
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload && sudo systemctl restart ollama
|
||||
```
|
||||
|
||||
## Access Control
|
||||
|
||||
By default, all devices on your tailnet can reach all other devices. For a personal homelab this is fine. If you want to restrict access, Tailscale ACLs (in the admin console) let you define which devices can reach which others.
|
||||
|
||||
Simple ACL example — allow all devices to reach all others (the default):
|
||||
|
||||
```json
|
||||
{
|
||||
"acls": [
|
||||
{"action": "accept", "src": ["*"], "dst": ["*:*"]}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
More restrictive — only your laptop can reach the server:
|
||||
|
||||
```json
|
||||
{
|
||||
"acls": [
|
||||
{
|
||||
"action": "accept",
|
||||
"src": ["tag:laptop"],
|
||||
"dst": ["tag:server:*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Subnet Router (Optional)
|
||||
|
||||
If you want all your home LAN devices accessible over Tailscale (not just devices with Tailscale installed), set up a subnet router on your home server:
|
||||
|
||||
```bash
|
||||
# Advertise your home subnet
|
||||
sudo tailscale up --advertise-routes=192.168.1.0/24
|
||||
|
||||
# Approve the route in the admin console
|
||||
# Then on client devices, enable accepting routes:
|
||||
sudo tailscale up --accept-routes
|
||||
```
|
||||
|
||||
Now any device on your home LAN is reachable from anywhere on the tailnet, even if Tailscale isn't installed on that device.
|
||||
|
||||
## Gotchas & Notes
|
||||
|
||||
- **Tailscale is not a replacement for a firewall.** It secures device-to-device communication, but your server still needs proper firewall rules for LAN access.
|
||||
- **Devices need to be approved in the admin console** unless you've enabled auto-approval. If a new device can't connect, check the admin console first.
|
||||
- **Mobile devices will disconnect in the background** depending on OS settings. iOS aggressively kills VPN connections. Enable background app refresh for Tailscale in iOS Settings.
|
||||
- **DERP relay adds latency** when direct connections aren't possible (common on cellular). Still encrypted and functional, just slower than direct peer-to-peer.
|
||||
- **Exit nodes** let you route all traffic through a specific tailnet device — useful as a simple home VPN if you want all your internet traffic going through your home IP when traveling. Set with `--advertise-exit-node` on the server and `--exit-node=hostname` on the client.
|
||||
|
||||
## See Also
|
||||
|
||||
- [[self-hosting-starter-guide]]
|
||||
- [[linux-server-hardening-checklist]]
|
||||
- [[setting-up-caddy-reverse-proxy]]
|
||||
Reference in New Issue
Block a user