Exploring Modern VPN Solutions: Tailscale vs Traditional Approaches

When I first needed to connect to my home server remotely, I thought setting up a VPN would be straightforward. Just install OpenVPN, configure some files, and I'd be done, right? Three days and countless configuration errors later, I realized the world of VPNs had evolved far beyond traditional solutions. This post explores my journey through both traditional and modern VPN approaches, and why solutions like Tailscale are revolutionizing remote connectivity.

My First Attempt: Traditional OpenVPN

Like many beginners, I started with OpenVPN because it was the most mentioned solution online. The experience was... educational, to put it mildly. Here's what setting up OpenVPN looked like:

Traditional OpenVPN Setup Processbash
1# Install OpenVPN and EasyRSA
2  sudo apt update
3  sudo apt install openvpn easy-rsa
4  
5  # Set up Certificate Authority
6  make-cadir ~/openvpn-ca
7  cd ~/openvpn-ca
8  source vars
9  ./clean-all
10  ./build-ca
11  ./build-key-server server
12  ./build-dh
13  openvpn --genkey --secret keys/ta.key
14  
15  # Create server configuration
16  sudo nano /etc/openvpn/server.conf
17  
18  # Server configuration (simplified)
19  port 1194
20  proto udp
21  dev tun
22  ca ca.crt
23  cert server.crt
24  key server.key
25  dh dh2048.pem
26  server 10.8.0.0 255.255.255.0
27  push "redirect-gateway def1 bypass-dhcp"
28  push "dhcp-option DNS 8.8.8.8"
29  keepalive 10 120
30  tls-auth ta.key 0
31  cipher AES-256-CBC
32  comp-lzo
33  max-clients 10
34  persist-key
35  persist-tun
36  status openvpn-status.log
37  verb 3
38  
39  # Generate client certificates
40  ./build-key client1
41  
42  # Create client configuration file
43  # (Another 30+ lines of configuration)

After hours of setup, I had a working VPN, but the complexity was overwhelming. Managing certificates, configuring firewall rules, setting up port forwarding on my router, and creating individual configuration files for each device felt like operating a small PKI infrastructure just to access my home server.

The Problems with Traditional VPNs

As I used OpenVPN for a few weeks, several pain points became apparent:

  • Certificate Management Hell: Every new device needed certificates generated, signed, and distributed securely
  • Port Forwarding Nightmares: Required router configuration and a static IP or dynamic DNS
  • Performance Issues: All traffic routed through my home connection, creating a bottleneck
  • Client Configuration: Each device needed a complex configuration file
  • Mobile Unfriendly: Battery drain and connection drops were constant issues
  • No Mesh Networking: Everything went through the central server, even local connections

Enter WireGuard: A Breath of Fresh Air

Frustrated with OpenVPN's complexity, I discovered WireGuard. The difference was night and day:

WireGuard: Simplicity Itselfbash
1# Install WireGuard
2  sudo apt install wireguard
3  
4  # Generate keys
5  wg genkey | tee privatekey | wg pubkey > publickey
6  
7  # Server configuration (/etc/wireguard/wg0.conf)
8  [Interface]
9  Address = 10.0.0.1/24
10  PrivateKey = <server-private-key>
11  ListenPort = 51820
12  
13  [Peer]
14  PublicKey = <client-public-key>
15  AllowedIPs = 10.0.0.2/32
16  
17  # Client configuration
18  [Interface]
19  Address = 10.0.0.2/24
20  PrivateKey = <client-private-key>
21  DNS = 8.8.8.8
22  
23  [Peer]
24  PublicKey = <server-public-key>
25  Endpoint = server.example.com:51820
26  AllowedIPs = 0.0.0.0/0
27  PersistentKeepalive = 25
28  
29  # Start WireGuard
30  sudo wg-quick up wg0

WireGuard's elegance was immediately apparent. The entire configuration fit on a single screen, used modern cryptography by default, and performed significantly better than OpenVPN. But even WireGuard had limitations—I still needed to manage keys manually and handle NAT traversal myself.

The Mesh VPN Revolution

Then I discovered mesh VPNs, and everything changed. Instead of all traffic flowing through a central server, devices connect directly to each other when possible. This approach offers several advantages:

Traditional VPN (Hub and Spoke)

    Device A
          ↓
      VPN Server ←→ Internet
          ↑
      Device B

Mesh VPN (Direct Connections)

Device A ←→ Device B
      ↓         ↓
      Device C ←→

Tailscale: VPN Made Simple

Tailscale was my introduction to how modern VPNs should work. Built on WireGuard, it adds automatic key management, NAT traversal, and mesh networking. Here's how simple the setup was:

Tailscale: VPN in 60 Secondsbash
1# Install Tailscale
2  curl -fsSL https://tailscale.com/install.sh | sh
3  
4  # Start and authenticate
5  sudo tailscale up
6  
7  # That's it! Really!
8  
9  # Check your network
10  tailscale status
11  
12  # Access other devices directly
13  ssh user@device-name
14  ping laptop
15  curl http://home-server:8080

The contrast was stark. What took hours with OpenVPN took literally one minute with Tailscale. No certificates, no port forwarding, no configuration files. It just worked.

How Modern VPNs Solve Traditional Problems

Modern solutions like Tailscale, ZeroTier, and Nebula solve traditional VPN problems elegantly:

1. NAT Traversal

Traditional VPNs require port forwarding and public IPs. Modern solutions use techniques borrowed from WebRTC:

  • STUN: Discovers your public IP and port
  • Direct connections: Devices find each other and connect directly
  • DERP relays: Fallback when direct connection isn't possible

2. Key Management

Instead of manually managing certificates:

  • Keys are generated automatically
  • Distributed through a control plane
  • Rotated without user intervention
  • Devices authenticate through SSO providers

3. Performance

Mesh networking means:

  • Direct peer-to-peer connections when possible
  • Lower latency for local connections
  • No central bottleneck
  • Automatic failover if one path fails

Comparing Modern VPN Solutions

I've tested several modern VPN solutions. Here's how they compare:

Tailscale

  • Pros: Easiest setup, great documentation, reliable NAT traversal
  • Cons: Requires third-party coordination server (or self-hosted Headscale)
  • Best for: Personal use, small teams, quick deployments

ZeroTier

  • Pros: Layer 2 networking, very mature, self-hostable
  • Cons: More complex than Tailscale, slower performance
  • Best for: Complex network topologies, bridging networks

Nebula (by Slack)

  • Pros: Fully open source, designed for scale, no third-party dependencies
  • Cons: More manual configuration, weaker NAT traversal
  • Best for: Large organizations, full control requirements

NetBird

  • Pros: Open source, good UI, self-hostable
  • Cons: Newer project, smaller community
  • Best for: Open source enthusiasts, privacy-conscious users

Real-World Use Cases

Here's how I use modern VPN solutions in practice:

My VPN Use Casesyaml
1# Home Lab Access
2  - Connect to home server from anywhere
3  - Access local services (Plex, Home Assistant)
4  - Remote development on powerful desktop
5  
6  # Multi-Device Sync
7  - Share files between laptop, desktop, phone
8  - Access development environments from any device
9  - Consistent workspace across locations
10  
11  # Secure Public WiFi
12  - Route traffic through home connection
13  - Access geo-restricted content
14  - Protect sensitive data on untrusted networks
15  
16  # Collaborative Development
17  - Share local development servers with team
18  - Pair programming with screen sharing
19  - Access staging environments securely
20  
21  # IoT Management
22  - Secure access to smart home devices
23  - Monitor security cameras remotely
24  - Update IoT devices without exposing them to internet

Self-Hosting: Taking Back Control with Headscale

While Tailscale's hosted solution is convenient, I wanted full control over my infrastructure. This led me to Headscale, an open-source implementation of the Tailscale control server. My journey into self-hosting Tailscale was both challenging and rewarding.

Setting Up Headscale

My Headscale Installation Processbash
1# Install Headscale on Ubuntu
2  wget https://github.com/juanfont/headscale/releases/download/v0.22.3/headscale_0.22.3_linux_amd64.deb
3  sudo dpkg -i headscale_0.22.3_linux_amd64.deb
4  
5  # Configure Headscale
6  sudo nano /etc/headscale/config.yaml
7  
8  # My working configuration:
9  server_url: https://headscale.example.com:8085
10  listen_addr: 0.0.0.0:8085
11  metrics_listen_addr: 127.0.0.1:9090
12  
13  # IP allocation
14  ip_prefixes:
15    - 100.64.0.0/10
16    - fd7a:115c:a1e0::/48
17  
18  # Database
19  db_type: sqlite3
20  db_path: /var/lib/headscale/db.sqlite
21  
22  # DERP configuration (more on this later)
23  derp:
24    server:
25      enabled: false
26    urls:
27      - https://controlplane.tailscale.com/derpmap/default
28    paths: []
29    auto_update_enabled: true
30    update_frequency: 24h
31  
32  # Start Headscale
33  sudo systemctl enable headscale
34  sudo systemctl start headscale
35  
36  # Create a namespace (user)
37  headscale namespaces create personal
38  
39  # Generate pre-auth key
40  headscale preauthkeys create --namespace personal --reusable --expiration 24h
41  
42  # Check status
43  headscale nodes list

Nginx Reverse Proxy for HTTPS

Getting HTTPS to work properly was crucial for Headscale:

Nginx Configuration for Headscalenginx
1server {
2      listen 80;
3      server_name headscale.example.com;
4      return 301 https://$server_name$request_uri;
5  }
6  
7  server {
8      listen 443 ssl http2;
9      listen [::]:443 ssl http2;
10      server_name headscale.example.com;
11  
12      ssl_certificate /etc/letsencrypt/live/headscale.example.com/fullchain.pem;
13      ssl_certificate_key /etc/letsencrypt/live/headscale.example.com/privkey.pem;
14  
15      # Headscale API
16      location / {
17          proxy_pass http://localhost:8085;
18          proxy_http_version 1.1;
19          proxy_set_header Upgrade $http_upgrade;
20          proxy_set_header Connection "upgrade";
21          proxy_set_header Host $server_name;
22          proxy_set_header X-Real-IP $remote_addr;
23          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
24          proxy_set_header X-Forwarded-Proto $scheme;
25          proxy_buffering off;
26          proxy_request_buffering off;
27          proxy_redirect http:// https://;
28      }
29  }

Connecting Clients to Headscale

Connecting Devices to My Headscale Serverbash
1# On client machines:
2  # Linux
3  tailscale up --login-server https://headscale.example.com:8085
4  
5  # macOS - had issues with network extension
6  tailscale up --login-server https://headscale.example.com:8085 --accept-routes
7  
8  # Using pre-auth key (for servers)
9  tailscale up --login-server https://headscale.example.com:8085 --authkey YOUR_KEY_HERE
10  
11  # Check connection
12  tailscale status
13  
14  # Common issues I faced:
15  # 1. Certificate errors - ensure valid HTTPS
16  # 2. Port 8085 blocked - check firewall
17  # 3. DNS issues - use IP first, then domain

Understanding DERP: The Magic Behind NAT Traversal

DERP (Designated Encrypted Relay for Packets) servers are what make Tailscale's magic work when direct connections fail. Understanding and potentially self-hosting DERP became my next challenge.

What DERP Actually Does

When two devices can't connect directly (due to strict NATs or firewalls), DERP servers relay the encrypted traffic. Think of them as a fallback post office when direct delivery isn't possible.

Investigating DERP Connectionsbash
1# Check if using DERP
2  tailscale status
3  # Look for relay "der" in the output
4  
5  # Test DERP server connectivity
6  curl https://derp.example.com/derp/probe
7  
8  # Monitor DERP usage
9  tailscale debug derp
10  
11  # See which DERP region you're using
12  tailscale netcheck
13  
14  # Example output:
15  Report:
16    * UDP: true
17    * IPv4: yes, PUBLIC_IP:PORT
18    * IPv6: no
19    * MappingVariesByDestIP: false
20    * DERP latency:
21      - sin: 32.2ms (Singapore)
22      - tok: 45.1ms (Tokyo)
23      - fra: 120.3ms (Frankfurt)

My DERP Server Experiments

I spent weeks trying to self-host DERP servers. Here's what I learned:

Attempting to Run My Own DERP Serverbash
1# Building DERP from source
2  git clone https://github.com/tailscale/tailscale.git
3  cd tailscale/cmd/derper
4  go build -o derper
5  
6  # Attempt 1: Basic DERP server
7  ./derper -hostname derp.example.com -a :443 -certmode manual     -certfile /etc/letsencrypt/live/derp.example.com/fullchain.pem     -keyfile /etc/letsencrypt/live/derp.example.com/privkey.pem
8  
9  # Attempt 2: DERP with STUN
10  ./derper -hostname derp.example.com -a :443 -stun -certmode letsencrypt
11  
12  # Attempt 3: Behind nginx (this was tricky!)
13  ./derper -hostname derp.example.com -a :8090 -stun-port 3478 -certmode manual
14  
15  # The nginx config that finally worked:
16  # Note: DERP needs WebSocket support
17  location /derp {
18      proxy_pass http://127.0.0.1:8090;
19      proxy_http_version 1.1;
20      proxy_set_header Upgrade $http_upgrade;
21      proxy_set_header Connection "upgrade";
22      proxy_set_header Host $host;
23      proxy_buffering off;
24      proxy_read_timeout 86400;
25  }
26  
27  # STUN needs UDP (can't proxy through nginx)
28  # Had to open port 3478 directly in firewall

DERP Configuration in Headscale

Integrating custom DERP servers with Headscale was complex:

Custom DERP Configurationyaml
1# Embedded DERP in Headscale (experimental)
2  derp:
3    server:
4      enabled: true
5      region_id: 999
6      region_code: "custom"
7      region_name: "My Region"
8      stun_listen_addr: "0.0.0.0:3478"
9  
10  # Using custom DERP map
11  derp:
12    server:
13      enabled: false
14    urls: []  # Disable default Tailscale DERP
15    paths:
16      - /etc/headscale/derp.json
17  
18  # My custom derp.json:
19  {
20    "regions": {
21      "900": {
22        "regionid": 900,
23        "regioncode": "myderp",
24        "regionname": "My DERP",
25        "nodes": [{
26          "name": "1",
27          "regionid": 900,
28          "hostname": "derp.example.com",
29          "ipv4": "YOUR_IP",
30          "stunport": 3478,
31          "stunonly": false,
32          "derpport": 443
33        }]
34      }
35    }
36  }

DERP Challenges and Lessons

My DERP journey taught me several hard lessons:

  • TLS is mandatory: DERP clients expect HTTPS, self-signed certificates don't work
  • Port conflicts: DERP wants port 443, but so does everything else
  • STUN requirements: UDP port 3478 must be accessible for NAT traversal
  • Bandwidth costs: DERP servers can use significant bandwidth when relaying
  • Monitoring is hard: Limited visibility into DERP performance
  • Updates break things: DERP protocol changes with Tailscale updates

My Current Setup

After all the experimentation, here's what actually works:

My Production Headscale + DERP Setupbash
1# Headscale with Tailscale's DERP servers
2  # (Most reliable option)
3  derp:
4    server:
5      enabled: false
6    urls:
7      - https://controlplane.tailscale.com/derpmap/default
8    auto_update_enabled: true
9    update_frequency: 24h
10  
11  # Firewall rules
12  sudo ufw allow 8085/tcp comment 'Headscale API'
13  sudo ufw allow 3478/udp comment 'STUN'
14  sudo ufw allow 443/tcp comment 'HTTPS'
15  
16  # Monitoring setup
17  # Prometheus metrics from Headscale
18  curl http://localhost:9090/metrics | grep headscale
19  
20  # Log aggregation
21  tail -f /var/log/headscale/headscale.log | grep -E "DERP|relay"
22  
23  # Backup script
24  #!/bin/bash
25  # Backup Headscale data
26  systemctl stop headscale
27  cp /var/lib/headscale/db.sqlite /backup/headscale-$(date +%Y%m%d).db
28  systemctl start headscale

Self-hosting the control plane with Headscale while using Tailscale's DERP infrastructure gave me the best balance of control and reliability. Running custom DERP servers remains an ongoing experiment—fascinating but not yet production-ready for my needs.

Performance Comparisons

I ran some informal benchmarks on my setup:

VPN Performance Teststext
1Test: File transfer between two devices on different networks
2  Baseline (no VPN): 95 Mbps
3  
4  OpenVPN (AES-256): 
5  - Throughput: 45 Mbps
6  - CPU usage: 60%
7  - Latency: +25ms
8  
9  WireGuard (raw):
10  - Throughput: 85 Mbps
11  - CPU usage: 15%
12  - Latency: +5ms
13  
14  Tailscale (direct connection):
15  - Throughput: 82 Mbps
16  - CPU usage: 18%
17  - Latency: +6ms
18  
19  Tailscale (DERP relay):
20  - Throughput: 50 Mbps
21  - CPU usage: 20%
22  - Latency: +30ms
23  
24  ZeroTier:
25  - Throughput: 70 Mbps
26  - CPU usage: 25%
27  - Latency: +10ms

WireGuard-based solutions consistently outperformed OpenVPN, with Tailscale's direct connections nearly matching raw WireGuard performance.

Lessons Learned

My journey through VPN technologies taught me several valuable lessons:

  • Simple is better: Complex configurations lead to security mistakes
  • Modern protocols matter: WireGuard's performance advantage is real
  • Mesh Hub-and-spoke: Direct connections improve everything
  • Automation is key: Manual key management doesn't scale
  • Consider your needs: Not everyone needs a complex setup
  • Test everything: What works for others might not work for you

Choosing the Right Solution

Here's my decision framework for choosing a VPN solution:

  • Just need it to work? → Tailscale
  • Need Layer 2 networking? → ZeroTier
  • Full control and scale? → Nebula or self-hosted Headscale
  • Learning networking? → Start with WireGuard
  • Legacy requirements? → OpenVPN (but consider modernizing)

The Future of VPNs

The shift from traditional to modern VPNs represents a fundamental change in how we think about secure networking. Instead of building walls and gates, we're creating secure, direct paths between devices. This approach aligns better with our modern, distributed world where the perimeter is everywhere and nowhere.

As I continue exploring these technologies, I'm excited about innovations like:

  • Better mobile support with improved battery life
  • Integration with service mesh technologies
  • Post-quantum cryptography preparations
  • Decentralized coordination servers
  • Native operating system integration
"Moving from OpenVPN to modern mesh VPNs was like upgrading from a flip phone to a smartphone. Both make calls, but one transforms how you think about communication. Modern VPNs don't just connect networks—they reimagine what secure networking can be."

💬 Comments & Discussion

Share your thoughts, ask questions, or discuss this post. Comments are powered by GitHub Discussions.