Privacy Tools Guide

Running multiple VPN connections simultaneously is a powerful technique for developers and power users who need to route different applications through separate network tunnels. Whether you’re testing applications across different regions, isolating development environments, or maintaining privacy while accessing location-specific services, concurrent VPN connections provide flexibility that single-tunnel solutions cannot match.

This guide covers practical methods for achieving concurrent VPN connections on Linux, macOS, and Windows, with emphasis on command-line approaches that integrate well with development workflows.

Understanding VPN Routing at the Network Level

Before implementing concurrent connections, you need to understand how network routing works. Each VPN creates a virtual network interface with its own IP address and routing table entries. By default, all traffic flows through the default route, but you can manipulate routing tables to send specific traffic through specific interfaces.

The key distinction is between split tunneling and full tunnel configurations. Split tunneling allows you to route only certain traffic through the VPN while letting other traffic use your regular connection. This is essential for running concurrent connections without routing all your traffic through every tunnel.

Method 1: Using Separate VPN Client Instances

The simplest approach involves running multiple VPN client instances, each configured with different routing rules. Most OpenVPN, WireGuard, and IKEv2 clients support configuration files that specify which traffic should traverse the tunnel.

Create separate configuration files for each connection:

# vpn-client-a.conf - Routes only specific subnet through this tunnel
dev tun0
remote vpn-server-a.example.com 1194
cipher AES-256-GCM
auth SHA256
route 10.8.0.0 255.255.255.0
route 172.16.0.0 255.240.0.0
persist-key
persist-tun
# vpn-client-b.conf - Different server, different routes
dev tun1
remote vpn-server-b.example.com 1194
cipher AES-256-GCM
auth SHA256
route 192.168.100.0 255.255.255.0
persist-key
persist-tun

Start each connection independently:

sudo openvpn --config vpn-client-a.conf &
sudo openvpn --config vpn-client-b.conf &

Each instance creates its own network interface, and the route directives ensure traffic to those networks goes through the appropriate tunnel.

Method 2: WireGuard with Custom Routing Tables

WireGuard offers a modern, high-performance alternative with straightforward routing configuration. Each WireGuard interface can have its own peer configuration with specific allowed IPs.

Create multiple WireGuard interfaces:

# /etc/wireguard/wg-client-a.conf
[Interface]
Address = 10.0.0.2/24
PrivateKey = <your-private-key>
ListenPort = 51820

[Peer]
PublicKey = <server-public-key>
Endpoint = vpn-server-a.example.com:51820
AllowedIPs = 10.0.0.0/24, 192.168.50.0/24
PersistentKeepalive = 25
# /etc/wireguard/wg-client-b.conf
[Interface]
Address = 10.0.1.2/24
PrivateKey = <your-private-key>
ListenPort = 51821

[Peer]
PublicKey = <server-public-key>
Endpoint = vpn-server-b.example.com:51821
AllowedIPs = 10.0.1.0/24, 172.16.0.0/24
PersistentKeepalive = 25

Activate both interfaces:

sudo wg-quick up wg-client-a
sudo wg-quick up wg-client-b

The AllowedIPs parameter controls what traffic each tunnel handles. Setting specific subnets rather than 0.0.0.0/0 enables split tunneling.

Method 3: Application-Level Routing with Proxy Chains

For fine-grained control at the application level, you can route specific processes through different tunnels without modifying system-wide routing. This approach is particularly useful for developers testing applications in different network environments.

Using proxychains with SOCKS Proxies

Configure your VPN clients to expose SOCKS proxies, then use proxychains to force specific applications through those proxies:

# Install proxychains
# macOS: brew install proxychains4
# Ubuntu: sudo apt install proxychains4

# Configure proxychains
# /etc/proxychains.conf or ~/.proxychains.conf
strict_chain
proxy_dns
tcp_read_time_out 15000
tcp_connect_time_out 8000

[ProxyList]
socks5 127.0.0.1 1080  # VPN Client A
socks5 127.0.0.1 1081  # VPN Client B

Run applications through specific proxies:

# Route curl through VPN A
proxychains4 curl https://api.example.com

# Route wget through VPN B
proxychains4 -f ~/.proxychains.conf wget http://resource.example.com

Using Linux Network Namespaces

Linux network namespaces provide complete network isolation. Create separate namespaces for different VPN connections:

# Create namespace for VPN A
sudo ip netns add vpn-namespace-a

# Create veth pair and move one end to namespace
sudo ip link add veth-a type veth peer name veth-a-peer
sudo ip link set veth-a-peer netns vpn-namespace-a

# Configure IP addresses
sudo ip addr add 10.200.0.1/24 dev veth-a
sudo ip netns exec vpn-namespace-a ip addr add 10.200.0.2/24 dev veth-a-peer

# Bring up interfaces
sudo ip link set veth-a up
sudo ip netns exec vpn-namespace-a ip link set veth-a-peer up
sudo ip netns exec vpn-namespace-a ip link set lo up

Run applications in specific namespaces:

# Run application in VPN A namespace
sudo ip netns exec vpn-namespace-a openvpn --config vpn-client-a.conf &
sudo ip netns exec vpn-namespace-a curl https://api.example.com

This approach provides complete isolation, ensuring no traffic leaks between namespaces.

Method 4: Routing Applications by UID on Linux

For system-level control on Linux, you can route traffic based on user or group IDs using iptables marks:

# Mark traffic from specific user
sudo iptables -t mangle -A OUTPUT -m owner --uid-owner developer -j MARK --set-mark 1

# Route marked traffic through specific interface
sudo ip rule add fwmark 1 table vpn-table-a
sudo ip route add default dev tun0 table vpn-table-a

This allows you to run applications under different user accounts and have each user’s traffic automatically routed through the appropriate VPN.

Practical Examples for Developers

Running Development Servers with Different VPN Contexts

# Terminal 1: Start VPN A and development server
sudo wg-quick up wg-client-a
cd project-a && npm run dev

# Terminal 2: Start VPN B and different service
sudo wg-quick up wg-client-b
cd project-b && python -m flask run --port 5001

Testing API Endpoints from Multiple Regions

# Script to test API from different VPN contexts
#!/bin/bash

# Test from VPN A
proxychains4 curl -s https://api.example.com/status | jq .

# Test from VPN B
proxychains4 -f ~/.proxychains-b.conf curl -s https://api.example.com/status | jq .

Security Considerations

When running concurrent VPN connections, several security factors require attention:

  1. DNS leaks: Ensure each VPN configuration includes DNS servers that route through the tunnel, not your ISP’s default servers.

  2. IPv6 leakage: Disable IPv6 on interfaces not using VPN tunnels, or configure VPN clients to handle IPv6 properly.

  3. Kill switches: Implement firewall rules that block traffic if a VPN connection drops unexpectedly.

  4. Split tunneling risks: Be cautious with split tunneling configurations—ensure you understand exactly which traffic flows through which tunnel.

Troubleshooting Common Issues

Problem: Traffic not routing through the correct tunnel

# Check routing tables
ip route show table all
sudo tcpdump -i tun0 -n

Problem: DNS resolution failing

# Verify DNS configuration
sudo resolvectl status
cat /etc/resolv.conf

Problem: Connection drops

# Enable persistent keepalive in your VPN config
# For WireGuard: PersistentKeepalive = 25
# For OpenVPN: persist-key persist-tun

Built by theluckystrike — More at zovo.one