What Developers Actually Need From a VPN

Consumer VPN marketing focuses on privacy theater. Developers have concrete, testable requirements.

First: a native Linux client with a non-GUI interface. If the Linux 'support' is a `.ovpn` file and a PDF, that provider is not for you. You need a daemon you can control with a CLI, integrate into systemd, and script around. A provider that ships a proper client lets you do things like `nordvpn set killswitch on` or `nordvpn connect us` without touching a GUI.

Second: split tunneling that works at the process or CIDR level. You do not want to route traffic to your internal CI/CD server through a VPN exit node in Frankfurt. You want to route public API calls through the VPN while keeping local network traffic direct. This requires either policy-based routing support or a client that can exempt specific subnets.

Third: connection stability for long-lived sessions. SSH sessions, `tmux` over mosh, database connections, and `rsync` transfers all break if the VPN reconnects mid-transfer. We tested this by running a 2GB `rsync` transfer and a persistent SSH tunnel simultaneously, then checking whether reconnection events terminated either session.

Fourth: port forwarding or static IP options for running exposed services. If you self-host a dev server, a webhook receiver, or a staging environment, you may need inbound ports. Not every provider offers this without a premium tier.

Fifth: DNS leak protection that does not conflict with your existing resolver setup. If you run a local `unbound` or `dnsmasq` instance, a VPN client that hardcodes its own resolver will silently break your internal DNS.

# Quick DNS leak test after connecting
dig +short myip.opendns.com @resolver1.opendns.com
curl -s https://ifconfig.me
# Both should return the VPN exit IP, not your ISP IP

NordVPN: Best Overall for Linux CLI Workflows

NordVPN ships a native Linux client (`nordvpn` version 3.18.x at time of testing) that installs cleanly on Debian, Ubuntu, Fedora, and RHEL-based systems. The daemon (`nordvpnd`) registers as a systemd service and survives reboots without manual intervention.

The CLI is scriptable. You can set the VPN to auto-connect on a specific server group, enable the kill switch, whitelist subnets, and toggle Meshnet all from the terminal. For DevOps pipelines, this matters. We scripted a pre-flight check that connects to a specific server, verifies the exit IP matches the expected country, and aborts the deployment if it does not.

Meshnet is the feature that separates NordVPN from most competitors for developer use cases. It lets you create a private overlay network between your own machines (and invited peers) using NordLynx. You get direct WireGuard-based connectivity between a laptop, a cloud VM, and a home server without exposing any of them to the public internet. We tested this on three nodes across two continents and got 180-210 Mbps throughput with sub-5ms additional latency overhead versus a direct connection.

Split tunneling on Linux is handled via the `--allowlist` flag for subnets and apps. It works on kernels 4.15 and above. We found it reliable with one caveat: if you use network namespaces in your container setup, you need to apply the allowlist rules before the namespace is created.

The kill switch (`nordvpn set killswitch on`) uses nftables rules under the hood. It correctly blocks traffic on reconnection events. During our 2GB rsync test, the rsync process paused during a forced reconnection and resumed cleanly. The SSH tunnel we had open on a different interface (covered by the allowlist) was unaffected.

NordVPN supports obfuscated servers for environments where standard WireGuard or OpenVPN traffic gets blocked - useful if you are working from a corporate network that does deep packet inspection. The obfuscation layer adds roughly 15-20ms latency in our tests.

Pricing sits at the mid-range. The six-device limit is the main constraint; if you have a laptop, a workstation, a home server, and two cloud VMs you want in Meshnet, you hit it. The Plus plan adds a password manager and file scanner you will not use, but the Basic plan covers everything a developer needs. https://nordvpn.com/?ref=PLACEHOLDER

# Install NordVPN on Debian/Ubuntu
curl -sSf https://downloads.nordcdn.com/apps/linux/install.sh | sh

# Add your user to the nordvpn group
sudo usermod -aG nordvpn $USER

# Login and connect
nordvpn login
nordvpn connect us

# Enable kill switch and disable DNS leak protection if using custom resolver
nordvpn set killswitch on
nordvpn set dns off

# Allowlist your internal network subnet
nordvpn whitelist add subnet 10.0.0.0/8

# Verify connection
nordvpn status

Mullvad: Best for Privacy-Critical Dev Environments

Mullvad does not require an account email. You get a randomly generated account number, pay with cash or Monero if you want, and that is it. For developers building privacy-sensitive applications or working under NDAs that prohibit logging personal data on third-party systems, this matters.

The Mullvad CLI (`mullvad` package, version 2024.6 on our test server) is clean. It supports WireGuard natively and lets you pin to specific servers by hostname, set custom DNS resolvers, and configure split tunneling by process name or IP range. The daemon integrates with systemd.

We found Mullvad's Linux client more stable than NordVPN's during rapid reconnection cycles - useful in flaky network environments. The WireGuard implementation had zero dropped SSH sessions across 48 hours of testing with forced reconnections every 30 minutes.

The downside: no Meshnet equivalent. If you need to link multiple machines, you are setting up your own WireGuard configuration or using a separate tool. Mullvad's port forwarding was removed in 2023, so inbound connections to your dev server are not possible without a workaround. If you need to expose a webhook endpoint or a staging server through the VPN, Mullvad is not the right choice.

At 5 EUR/month flat with no annual discount, it costs more per month than NordVPN on a multi-year plan. But there are no tiers, no upsells, and no accounts to compromise.

# Install Mullvad on Debian/Ubuntu
curl -fsSLo /usr/share/keyrings/mullvad-keyring.asc https://repository.mullvad.net/deb/mullvad-keyring.asc
echo "deb [signed-by=/usr/share/keyrings/mullvad-keyring.asc arch=$( dpkg --print-architecture )] https://repository.mullvad.net/deb/stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/mullvad.list
sudo apt update && sudo apt install mullvad-vpn

# Connect with WireGuard to a specific server
mullvad relay set tunnel-protocol wireguard
mullvad relay set location us nyc
mullvad connect

# Set custom DNS (e.g., keep using local unbound)
mullvad dns set custom 127.0.0.1
// advertisement

Tailscale: Best for Private Dev Infrastructure Mesh

Tailscale is not a traditional VPN. It does not route your public traffic through an exit node by default. What it does is create a WireGuard mesh between all your devices using a control plane you authenticate against (Tailscale's coordination server, or self-hosted Headscale).

For a DevOps engineer managing a fleet of development machines, staging servers, and cloud VMs, Tailscale solves a real problem: how do you SSH into a VM that is behind NAT without opening public ports? You install Tailscale on both machines, they find each other via DERP relay if direct UDP is blocked, and you get stable connectivity.

Tailscale integrates with ACLs defined in HuJSON. You can write rules that say 'developers can reach port 22 on all tagged:server nodes, but not port 5432'. This is infrastructure-as-code for network access control. If you are already using tools like those at taskbotshub.ai for automating your DevOps pipelines, Tailscale ACL files slot in naturally as version-controlled policy.

The free tier covers 100 devices and 3 users - enough for most solo developers and small teams. The `tailscale` CLI ships for Linux, and `tailscaled` runs as a systemd service.

The limitation: if you need to anonymize your traffic or access geo-restricted resources, you need to configure an exit node yourself. That means one of your Tailscale nodes must have public internet access and be configured to advertise itself as an exit node. It works, but it is more setup than a traditional VPN.

# Install Tailscale on Linux
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

# Advertise a node as exit node (run on the node with public internet)
sudo tailscale up --advertise-exit-node

# Use that node as exit on your client
sudo tailscale up --exit-node=

# Check status
tailscale status
tailscale ping 

Split Tunneling Deep Dive: Routing Only What You Need

Every VPN on this list supports split tunneling in some form, but the implementation quality varies enough to affect real workflows.

NordVPN's whitelist approach is subnet-based on Linux. You specify CIDRs that bypass the VPN. This works well for keeping internal infrastructure traffic off the VPN while routing all other traffic through it.

Mullvad supports both subnet-based and application-based split tunneling. You can exclude a specific binary (e.g., your local `git` daemon or an internal API client) from VPN routing. This is more granular and harder to misconfigure.

If you need full manual control, you can implement split tunneling yourself with `ip rule` and `ip route`. The following example routes traffic to a specific IP through your ISP while sending everything else through the VPN tun interface. This approach works regardless of which VPN you use and survives client updates that might reset provider-managed rules.

# Manual split tunneling example
# Route traffic to 203.0.113.0/24 via ISP gateway, everything else via VPN

# Add routing table for bypass traffic
echo '200 bypass' >> /etc/iproute2/rt_tables

# Get your ISP gateway (before VPN connects)
ISP_GW=$(ip route | grep default | awk '{print $3}')
ISP_IFACE=$(ip route | grep default | awk '{print $5}')

# Add rule and route
ip rule add to 203.0.113.0/24 lookup bypass
ip route add default via $ISP_GW dev $ISP_IFACE table bypass

# Verify
ip route get 203.0.113.1
ip route get 8.8.8.8

VPN and DNS: Keeping Your Local Resolver Intact

Most VPN clients aggressively take over DNS. On Linux, this usually means writing to `/etc/resolv.conf` or pushing a new nameserver via `systemd-resolved`. If you run a local `unbound` resolver for internal hostnames or to filter DNS over your dev infrastructure, this will break your setup silently.

The safe approach varies by provider. With NordVPN, run `nordvpn set dns off` to prevent the client from managing DNS at all. Then ensure your existing resolver is configured to handle VPN-routed queries correctly. If you use `unbound` with forwarding zones, add a zone for your VPN provider's DNS if you need their ad-blocking, otherwise leave it off.

With Mullvad, `mullvad dns set custom 127.0.0.1` pins DNS to your local resolver.

For `systemd-resolved` users, check what NordVPN or Mullvad push during connection with `resolvectl status` before and after connecting. If you see the VPN's nameserver taking over your global DNS, that is the problem. You can lock your resolver configuration by making `/etc/resolv.conf` a regular file (not a symlink to `../run/systemd/resolve/stub-resolv.conf`) and writing your preferred nameservers directly.

If you are naming internal services, dev projects, or staging environments and want stable, meaningful hostnames, getting your naming convention right matters as much as your DNS setup. A service like nicename.me can help you find available domain names for project registration before you lock in your internal naming scheme.

# Check what your resolver looks like before and after VPN connection
resolvectl status

# Lock /etc/resolv.conf to your preferred config
sudo rm /etc/resolv.conf
sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolv.conf'
sudo chattr +i /etc/resolv.conf

# Verify DNS is not leaking after VPN connects
curl -s https://ipleak.net/json/ | python3 -m json.tool | grep -E 'ip|country_code'
// advertisement

Performance Numbers: What We Actually Measured

All tests ran from a Hetzner CX21 (2 vCPU, 4GB RAM, Debian 12) in Helsinki to servers in New York. Baseline throughput without VPN: 940 Mbps down, 820 Mbps up. We used `iperf3` for throughput and `ping` with 1000 packets for latency.

NordVPN (NordLynx/WireGuard): 680 Mbps down, 590 Mbps up. Latency overhead: +4ms median. SSH session survived 5/5 forced reconnection events.

Mullvad (WireGuard): 710 Mbps down, 620 Mbps up. Latency overhead: +3ms median. SSH session survived 5/5 forced reconnection events. Most stable in reconnection testing.

Tailscale (WireGuard, exit node in same DC): 760 Mbps down, 680 Mbps up. Latency overhead: +2ms when DERP relay is not involved. Drops to +40ms when forced through DERP relay due to NAT traversal failure.

NordVPN obfuscated servers: 280 Mbps down, 240 Mbps up. Latency overhead: +18ms. Use only when standard protocols are blocked.

For typical developer workflows (API calls, SSH, git push/pull, package downloads), all three perform identically in practice. The throughput differences only matter for large file transfers or video streaming.

# Reproduce our throughput test
# On the remote server (replace IP with your VPN exit or test server)
iperf3 -s

# On your local machine
iperf3 -c  -t 30 -P 4

# Latency baseline
ping -c 1000  | tail -2

Automation: Scripting VPN State in CI/CD Pipelines

If you are running any kind of automated deployment or test pipeline that needs VPN connectivity (accessing a private staging environment, pulling from a geo-restricted package mirror, or running compliance scans from a specific country), you need a VPN that can be controlled non-interactively.

NordVPN's CLI handles this well. After the initial `nordvpn login` (which requires a browser-based auth flow once), subsequent connections are non-interactive. You can call `nordvpn connect` in a shell script, check exit status, and proceed. The token-based login method (`nordvpn login --token `) allows fully automated setup without a browser.

For GitLab CI or GitHub Actions pipelines running on self-hosted runners on Linux, you can install the NordVPN daemon as part of runner setup and call the CLI from pipeline steps. We use this pattern for smoke tests against a staging API that is IP-allowlisted to specific regions.

If you are building more complex automation around your infrastructure, integrating VPN state management with orchestration tooling is worth looking at. Platforms like taskbotshub.ai focus on automating DevOps workflows and can wrap VPN connection checks as health conditions for deployment gates.

One pattern we use: a wrapper script that connects the VPN, verifies the exit IP matches the expected value, runs the target command, then disconnects. This ensures no step in the pipeline runs unprotected if the VPN fails to connect.

#!/bin/bash
# vpn-exec.sh - Run a command through VPN, abort if VPN fails
set -euo pipefail

VPN_COUNTRY="us"
EXPECTED_IP_PREFIX="104."  # Adjust to your expected exit range

# Connect
nordvpn connect "$VPN_COUNTRY"
sleep 3

# Verify exit IP
ACTUAL_IP=$(curl -s https://ifconfig.me)
if [[ "$ACTUAL_IP" != "$EXPECTED_IP_PREFIX"* ]]; then
  echo "ERROR: VPN exit IP $ACTUAL_IP does not match expected prefix $EXPECTED_IP_PREFIX"
  nordvpn disconnect
  exit 1
fi

echo "Connected via $ACTUAL_IP, running command..."
"$@"
EXIT_CODE=$?

nordvpn disconnect
exit $EXIT_CODE

# Usage: ./vpn-exec.sh curl https://internal-api.example.com/health