WireGuard vs Tailscale for Homelab Remote Access: Full Setup Guide (2026)

Secure remote access to your homelab without port forwarding. Set up WireGuard and Tailscale, understand when to use each, and lock down your Linux servers.

WireGuard and Tailscale configuration comparison on a Linux homelab — terminal showing active VPN tunnel status

Your homelab is only useful if you can reach it. But opening ports on your router to the public internet is how homelabs get compromised, cryptomined, or turned into spam relays.

There is a better way. Two of them, actually.

WireGuard is a modern VPN protocol built directly into the Linux kernel. Fast, minimal, cryptographically solid. You control everything — the server, the keys, the routing.

Tailscale is WireGuard with a coordination layer on top. It handles key exchange, NAT traversal, and device management for you. Setup takes minutes instead of hours.

This guide covers both, completely. By the end you will understand the tradeoffs, have both running, and know exactly which one to use for each situation in your homelab.


The Core Problem Both Solve

Your homelab sits behind your home router. To reach it from outside — from your phone, your work laptop, a hotel Wi-Fi — you have three options:

Option A — Open a port on your router (bad)

Internet → router:22 → your server SSH

Every port scanner on the internet will find it within hours. Your SSH logs will fill with brute-force attempts. Eventually someone gets in.

Option B — VPN tunnel (good)

Internet → VPN server → encrypted tunnel → your homelab

Only authenticated devices can connect. No ports exposed. The surface area for attack is a single authenticated endpoint.

Option C — Tailscale mesh (also good, different tradeoffs)

Device A ←→ Tailscale coordination ←→ Device B
         ↘ direct peer-to-peer connection ↗

No VPN server to maintain. Devices connect directly to each other through NAT, authenticated by Tailscale’s control plane.

Both B and C are the right answer. Which one depends on your requirements.


WireGuard vs Tailscale: When to Use Which

WireGuardTailscale
Setup time30–60 min5 min
External dependencyNone — fully self-hostedTailscale’s control plane
Works through strict NATNeeds port forwardYes — no port forward needed
Device limit (free)Unlimited3 devices (free), 20 on Personal
Subnet routingManual configBuilt-in, 2 clicks
Exit node (route all traffic)Manual configBuilt-in
AuditabilityComplete — you own everythingPartial — key exchange via Tailscale
Best forProduction, privacy-first, full controlRapid access, many devices, simplicity

My actual setup: Tailscale for daily access (phone, laptop, quick remote work). WireGuard for the always-on tunnel from a VPS, and for anything where I don’t want any external dependency.

You don’t have to choose. Run both. They coexist without conflict.


Part 1 — WireGuard

Step 1 — Install WireGuard

On the server (Ubuntu/Debian):

sudo apt update
sudo apt install wireguard wireguard-tools -y

# Verify kernel module is loaded
lsmod | grep wireguard
# Should print: wireguard   ...

WireGuard is built into the Linux kernel since 5.6. On Ubuntu 22.04+ it’s already there — the package just adds the userspace tools.


Step 2 — Generate Key Pairs

WireGuard uses public-key cryptography. Every peer — server and client — needs its own keypair.

# Generate server keys
cd /etc/wireguard
umask 077                                          # restrict permissions
wg genkey | tee server_private.key | wg pubkey > server_public.key

# Generate a client keypair (do this for each device)
wg genkey | tee client1_private.key | wg pubkey > client1_public.key

# View the keys
cat server_private.key    # keep this secret
cat server_public.key     # share this with clients
cat client1_private.key   # keep this secret
cat client1_public.key    # share this with the server

Never share a private key. The private key stays on the device that generated it. Only public keys are exchanged between peers.


Step 3 — Server Configuration

sudo nano /etc/wireguard/wg0.conf
[Interface]
# The VPN IP address for this server
Address = 10.10.0.1/24

# Port WireGuard listens on (open this on your router if behind NAT)
ListenPort = 51820

# Your server's private key
PrivateKey = <contents of server_private.key>

# Enable IP forwarding so clients can route through the server
PostUp   = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# ── Peer: Laptop ─────────────────────────────────────────────
[Peer]
# Client's public key
PublicKey = <contents of client1_public.key>

# IP this client gets on the VPN
AllowedIPs = 10.10.0.2/32

# ── Peer: Phone ──────────────────────────────────────────────
[Peer]
PublicKey = <phone_public_key>
AllowedIPs = 10.10.0.3/32

Replace eth0 with your actual network interface name — check with ip link.

Enable IP forwarding permanently:

echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Step 4 — Start WireGuard

# Start the interface
sudo wg-quick up wg0

# Enable on boot
sudo systemctl enable wg-quick@wg0

# Check status
sudo wg show

wg show prints every peer, their public key, their allowed IPs, and when they last sent a handshake. If you see latest handshake: X seconds ago after connecting a client — the tunnel is live.

Open the firewall:

sudo ufw allow 51820/udp
sudo ufw reload

If your homelab is behind a home router, also add a port forward in your router settings: UDP port 51820 → your server’s LAN IP.


Step 5 — Client Configuration (Linux laptop)

sudo apt install wireguard -y
sudo nano /etc/wireguard/wg0.conf
[Interface]
# This client's VPN IP
Address = 10.10.0.2/24

# This client's private key
PrivateKey = <contents of client1_private.key>

# DNS server to use when connected (optional — use your server's VPN IP)
DNS = 10.10.0.1

[Peer]
# Server's public key
PublicKey = <contents of server_public.key>

# Your server's public IP (or dynamic DNS hostname) + WireGuard port
Endpoint = YOUR.PUBLIC.IP.ADDRESS:51820

# Route homelab traffic through the VPN
# Use 0.0.0.0/0 to route ALL traffic through the tunnel (full VPN)
# Use 10.10.0.0/24,192.168.1.0/24 to route only homelab traffic (split tunnel)
AllowedIPs = 10.10.0.0/24, 192.168.1.0/24

# Keep the connection alive through NAT
PersistentKeepalive = 25

Connect:

sudo wg-quick up wg0

# Test — ping the server over the VPN
ping 10.10.0.1

# Check connection
sudo wg show

Step 6 — Client Configuration (Phone)

Install the WireGuard app from your phone’s app store. The easiest way to configure it is with a QR code.

On the server, generate a QR code for the phone config:

# Install qrencode
sudo apt install qrencode -y

# Create the phone config
cat > /tmp/phone.conf << 'EOF'
[Interface]
Address = 10.10.0.3/24
PrivateKey = <phone_private_key>
DNS = 10.10.0.1

[Peer]
PublicKey = <server_public_key>
Endpoint = YOUR.PUBLIC.IP:51820
AllowedIPs = 10.10.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25
EOF

# Display as QR code
qrencode -t ansiutf8 < /tmp/phone.conf

# Clean up the temp file (contains private key)
rm /tmp/phone.conf

On your phone: WireGuard app → +Scan QR code → scan → connect.


Step 7 — Dynamic DNS (if you don’t have a static IP)

Most home ISPs give you a dynamic IP that changes occasionally. Without a static IP, your WireGuard Endpoint goes stale.

Free solution — DuckDNS:

# Install curl if needed
sudo apt install curl -y

# Create update script
cat > /usr/local/bin/duckdns-update.sh << 'EOF'
#!/bin/bash
curl -s "https://www.duckdns.org/update?domains=YOURSUBDOMAIN&token=YOUR_TOKEN&ip=" \
  >> /var/log/duckdns.log 2>&1
EOF

chmod +x /usr/local/bin/duckdns-update.sh

# Run every 5 minutes via cron
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/local/bin/duckdns-update.sh") | crontab -

Get your token and subdomain at duckdns.org (free). Your WireGuard endpoint becomes yoursubdomain.duckdns.org:51820 — works even when your home IP changes.


Part 2 — Tailscale

Step 1 — Install Tailscale

curl -fsSL https://tailscale.com/install.sh | sh

That’s it. One line installs the daemon and CLI on any Debian/Ubuntu system.


Step 2 — Authenticate

sudo tailscale up

This prints an authentication URL. Open it in a browser, log in with Google, GitHub, or email — your choice. The machine is now part of your Tailscale network (called a tailnet).

Check it worked:

tailscale status

You’ll see your machine listed with a 100.x.x.x Tailscale IP. Every device on your tailnet gets a stable IP in the 100.64.0.0/10 range — these never change, even if your home IP does.


Step 3 — Add More Devices

Install Tailscale on every device you want on the network — same one-liner or the app store version for phones. After tailscale up and authentication, all devices can reach each other via their 100.x.x.x IPs.

No port forwarding. No router config. Tailscale handles NAT traversal using the DERP relay network as fallback when direct connection isn’t possible.


Step 4 — Access Your Entire Homelab via Subnet Routing

By default, Tailscale only connects the devices that have it installed. Subnet routing lets one machine act as a gateway so you can reach your entire LAN — including devices that don’t have Tailscale.

On your homelab server:

# Advertise your LAN subnet
sudo tailscale up --advertise-routes=192.168.1.0/24

# Enable IP forwarding (required)
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

In the Tailscale admin console (tailscale.com/admin):

  • Find your server → click the three dots → Edit route settings
  • Enable the advertised subnet

Now from any Tailscale device you can reach 192.168.1.x — your Proxmox web UI, your OpenMediaVault, your printers, everything — without installing Tailscale on each one.


Step 5 — MagicDNS (Access by Hostname)

Tailscale can give every machine a DNS name so you don’t have to remember IPs.

In the admin console: DNS → enable MagicDNS.

Now instead of ssh user@100.64.x.x you can:

ssh user@homelab-server
ssh user@proxmox

And in your browser: http://proxmox:8006 opens the Proxmox web UI from anywhere on your tailnet.


Step 6 — Serve a Local Service Publicly (with HTTPS)

Tailscale Serve exposes a local service on your tailnet with automatic HTTPS — the same feature used in the Ollama guide.

# Expose Open WebUI on your tailnet
tailscale serve --bg https / http://localhost:3000

# Expose Grafana
tailscale serve --bg https:3001 / http://localhost:3001

# List what's being served
tailscale serve status

Each service gets a URL like https://your-machine.tail1234.ts.net with a valid TLS certificate. No reverse proxy, no Let’s Encrypt setup, no Nginx config.

For public exposure (outside your tailnet):

tailscale funnel --bg https / http://localhost:3000

Funnel makes the service reachable from the public internet — useful for sharing a demo or webhook endpoint temporarily. Turn it off when done:

tailscale funnel --bg off

Step 7 — SSH via Tailscale (No Keys Needed)

Tailscale SSH replaces key-based SSH auth with Tailscale identity. No more managing authorized_keys.

# Enable Tailscale SSH on the server
sudo tailscale up --ssh

From any other Tailscale device:

# Connect using your Tailscale username
ssh user@homelab-server

Tailscale handles authentication. Access is controlled through the admin console — you can restrict which users can SSH into which machines, require re-authentication for sensitive machines, and review SSH session logs.


Part 3 — General SSH Hardening

Whether you use WireGuard, Tailscale, or both — your SSH config should be hardened regardless. These settings reduce your attack surface significantly.

sudo nano /etc/ssh/sshd_config
# Disable root login — never allow direct root SSH
PermitRootLogin no

# Disable password auth — keys or Tailscale only
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no

# Only allow specific users (replace with your username)
AllowUsers yourusername

# Use only modern key types
PubkeyAcceptedKeyTypes ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256

# Disconnect idle sessions after 10 minutes
ClientAliveInterval 300
ClientAliveCountMax 2

# Reduce login grace time
LoginGraceTime 20

# Disable X11 and other unnecessary features
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no

# Limit authentication attempts
MaxAuthTries 3
MaxSessions 5

Apply the changes:

sudo systemctl reload sshd

# Test in a NEW terminal before closing your current session
ssh -i ~/.ssh/your_key yourusername@yourserver

Always test in a new terminal. If something is misconfigured you’ll lock yourself out — the existing session keeps you in while you fix it.


Part 4 — UFW Firewall Baseline

A proper firewall baseline complements both VPN setups.

# Default: deny all incoming, allow all outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH — only from your LAN and Tailscale subnet
sudo ufw allow from 192.168.1.0/24 to any port 22
sudo ufw allow from 100.64.0.0/10 to any port 22   # Tailscale range

# WireGuard port (if using WireGuard)
sudo ufw allow 51820/udp

# Homelab services — LAN only
sudo ufw allow from 192.168.1.0/24 to any port 8006   # Proxmox
sudo ufw allow from 192.168.1.0/24 to any port 3001   # Grafana
sudo ufw allow from 192.168.1.0/24 to any port 9090   # Prometheus
sudo ufw allow from 192.168.1.0/24 to any port 5678   # n8n

# Enable
sudo ufw enable

# Review
sudo ufw status verbose

With this in place: SSH is only reachable from your LAN and your Tailscale devices. All homelab services are LAN-only. WireGuard handles external access. The internet sees a machine with no open ports except optionally WireGuard’s UDP port.


Checking Everything is Working

WireGuard:

# Server — see connected peers
sudo wg show

# Client — verify tunnel is up
sudo wg show

# Test connectivity through tunnel
ping 10.10.0.1            # ping the server VPN IP from client
ssh user@10.10.0.1        # SSH over VPN
curl http://10.10.0.1:3001  # reach Grafana over VPN

Tailscale:

# See all devices on your tailnet
tailscale status

# Check connectivity to another device
tailscale ping homelab-server

# View your Tailscale IP
tailscale ip -4

# Check routes being advertised/accepted
tailscale status --json | jq '.Peer[] | {name: .HostName, routes: .PrimaryRoutes}'

Troubleshooting

WireGuard: handshake never completes:

# Check if WireGuard port is reachable from outside
# (run this from a different network, e.g. phone hotspot)
nc -zvu YOUR.PUBLIC.IP 51820

# Check firewall
sudo ufw status | grep 51820

# Check if your public IP is correct
curl https://api.ipify.org

Most WireGuard connection failures are: wrong public IP in the client Endpoint, firewall blocking UDP 51820, or missing port forward on the router.

WireGuard: connected but can’t reach LAN devices:

IP forwarding is probably off:

cat /proc/sys/net/ipv4/ip_forward
# Should be 1 — if it's 0:
sudo sysctl -w net.ipv4.ip_forward=1

Tailscale: devices connected but can’t reach each other:

# Check if direct connection is established or going through relay
tailscale ping --verbose homelab-server
# Look for "direct" vs "via DERP relay"

If stuck on relay, the devices are behind symmetric NAT. This is normal for some ISPs — Tailscale still works, just slightly higher latency.

Tailscale: subnet routes not working:

Make sure you approved the routes in the admin console AND have IP forwarding enabled on the subnet router machine. Both are required.


External access
  └── Tailscale — phone, laptop, quick remote work
      └── MagicDNS for human-readable hostnames
      └── SSH via Tailscale on sensitive machines

Always-on tunnel
  └── WireGuard — VPS → homelab permanent link
      └── Useful for self-hosted services that need stable external IPs

Firewall (UFW)
  └── Default deny incoming
  └── SSH from LAN + Tailscale range only
  └── All services LAN-only
  └── WireGuard UDP port open

SSH hardening
  └── No root login
  └── No password auth
  └── Key-only or Tailscale SSH

With this stack in place your homelab is reachable from anywhere, locked down from the internet, and auditable. Your attack surface is essentially zero — there are no open ports for scanners to find, and no passwords to brute-force.

That is how you run a homelab that stays yours.

Frequently Asked Questions

Do I need to open ports in my router for WireGuard or Tailscale?
WireGuard requires one UDP port forwarded from your router to the server (default 51820). Tailscale requires no port forwarding at all — it uses NAT traversal to punch through firewalls, making it the simpler option for most homelab setups.
Is Tailscale free?
Tailscale is free for personal use with up to 3 users and 100 devices. For a homelab, the free tier is more than sufficient. The paid plans add more users, RBAC, and compliance features that most homelabbers don't need.
What is the difference between WireGuard and Tailscale?
WireGuard is a VPN protocol you configure yourself — you manage keys, IP addressing, routing, and peer configs. Tailscale builds on WireGuard but automates all of that: key exchange, NAT traversal, and device management happen automatically. WireGuard gives more control; Tailscale is faster to set up.
Can I use WireGuard and Tailscale simultaneously on the same server?
Yes. They use different network interfaces (wg0 for WireGuard, tailscale0 for Tailscale) and can coexist without conflict. A common pattern is to use Tailscale for everyday access and keep WireGuard as a fallback for cases where Tailscale's relay servers are unavailable.

Get notified when new articles and designs land:

No spam. Unsubscribe any time.

Sergej Voronko
Sergej Voronko
SAP Basis · Senior Operations Manager · Linux infrastructure engineer
About the author →

[discussion]

Comments are powered by Giscus — backed by GitHub Discussions. Sign in with GitHub to join the conversation.