Blueprint: Migrating Gluetun & PIA from OpenVPN to WireGuard
A technical reference, trade-off analysis, and step-by-step implementation guide for running Private Internet Access (PIA) with WireGuard and dynamic port forwarding on Unraid via Gluetun.
1. Context & Motivation
Current Benchmark Baseline (OpenVPN)
- Host Direct Speed: ~1.35 Gbps (~169 MB/s on Gigabit+ Fiber ISP)
- Current Gluetun OpenVPN Tunnel: ~400 Mbps (~48.5 MB/s sustained)
- Current Status: 100% stable, fully automated port forwarding (port 23096), zero maintenance.
Why Switch to WireGuard?
- Throughput Potential: Increases tunnel speed from ~400 Mbps to 700 Mbps - 1+ Gbps (~90-120+ MB/s).
- Efficiency: Lower CPU utilization, zero userspace-to-kernel context switching overhead, and modern ChaCha20-Poly1305 encryption.
- When to Do This: If you routinely download massive 4K remuxes (50-100 GB) and want to saturate gigabit download speeds.
2. The Core Challenge: Why PIA is Unique
Unlike VPN providers like Mullvad, AirVPN, or Proton that provide static WireGuard private keys and configuration files in their user dashboards, Private Internet Access does not support static WireGuard keys.
Instead, PIA uses a dynamic, ephemeral REST API handshake:
sequenceDiagram
participant Host as Unraid / Helper Script
participant PIAToken as PIA Token API (HTTPS)
participant PIAGateway as PIA Montreal Gateway (:1337)
participant Gluetun as Gluetun Container
Host->>PIAToken: POST /api/client/v2/token (User + Pass)
PIAToken-->>Host: Ephemeral Auth Token
Host->>Host: Generate Local WG Keypair (wg genkey)
Host->>PIAGateway: POST /addKey?pt=<token>&pubkey=<pubkey>
PIAGateway-->>Host: Assigned IP, Server Pubkey, Gateway IP
Host->>Gluetun: Write wg0.conf into appdata
Gluetun->>PIAGateway: Establish WireGuard Handshake (UDP)
Why Gluetun Throws an Error Out-of-the-Box
When you set VPN_SERVICE_PROVIDER="private internet access" and VPN_TYPE="wireguard", Gluetun returns:
VPN provider name is not valid for wireguard: private internet access must be one of airvpn, custom, ivpn, mullvad, protonvpn...
Because Gluetun is strictly designed to isolate and killswitch all network traffic before a tunnel is up, it refuses to perform un-tunneled dynamic HTTP API registrations on boot. Therefore, PIA + WireGuard must be configured as a custom provider using a pre-generated configuration.
3. Architecture Blueprint: The Helper Pattern
To run WireGuard with PIA on Gluetun, you use a helper mechanism to generate and maintain the ephemeral WireGuard configuration and handle port forwarding renewals.
flowchart TD
subgraph Host["Unraid Server (10.0.0.x)"]
Helper["PIA WG Helper<br/>(Docker Container or Script)"]
AppData["/mnt/user/appdata/gluetun/wireguard/wg0.conf"]
Gluetun["Gluetun Container<br/>(VPN_SERVICE_PROVIDER=custom)<br/>(VPN_TYPE=wireguard)"]
qBit["qBittorrent Container<br/>(--net=container:gluetun)"]
end
PIAAPI["PIA Token & Gateway API"]
VPNServer["PIA Montreal WireGuard Server"]
Helper -->|1. Authenticate & Register Key| PIAAPI
Helper -->|2. Writes Config| AppData
AppData -->|3. Mounted Into| Gluetun
Gluetun -->|4. WireGuard Tunnel| VPNServer
qBit --> Gluetun
4. Step-by-Step Implementation Guide
When you are ready to make the switch, follow these steps:
Step 1: Deploy the PIA WireGuard Config Helper
You can use the community-standard kylegrantlucas/pia-wg-config or thrn/pia-wg container to generate and refresh the config file.
Create an Unraid User Script or run the helper container:
docker run --rm \
-e USER="pXXXXXXX" \
-e PASSWORD="your_secure_password" \
-e REGION="ca_montreal" \
-v /mnt/user/appdata/gluetun/wireguard:/pia \
thrn/pia-wg:latest
This generates:
/mnt/user/appdata/gluetun/wireguard/wg0.conf- Extracts the assigned local IP (e.g.,
10.x.x.x), private key, and Montreal gateway endpoint.
Step 2: Update the Gluetun Unraid Template (my-gluetun.xml)
Switch Gluetun's configuration to use the custom WireGuard configuration:
- Change Provider & Type:
VPN_SERVICE_PROVIDER=customVPN_TYPE=wireguard
- Mount the Config File:
- Host Path:
/mnt/user/appdata/gluetun/wireguard/wg0.conf - Container Path:
/gluetun/wireguard/wg0.conf(Mode:ro)
- Host Path:
- Alternative: Environment Variable Passthrough (No File Mount):
If you extract the variables directly fromwg0.conf, you can set:WIREGUARD_PRIVATE_KEY=<private_key_string>WIREGUARD_ADDRESSES=<assigned_internal_ip>/32WIREGUARD_ENDPOINT_IP=<montreal_ip>WIREGUARD_ENDPOINT_PORT=1337WIREGUARD_PUBLIC_KEY=<pia_server_public_key>
Step 3: WireGuard Dynamic Port Forwarding
On WireGuard, PIA requires refreshing the port forward signature every 15 minutes by communicating with their internal gateway IP:
# 1. Fetch Signature and Port
curl -s -G --data-urlencode "token=$TOKEN" \
"http://10.0.222.1:1337/getSignature" > /tmp/pia_signature.json
PORT=$(jq -r '.port' /tmp/pia_signature.json)
SIGNATURE=$(jq -r '.signature' /tmp/pia_signature.json)
PAYLOAD=$(jq -r '.payload' /tmp/pia_signature.json)
# 2. Bind Port (Keepalive Loop every 15 minutes)
curl -s -G --data-urlencode "payload=$PAYLOAD" \
--data-urlencode "signature=$SIGNATURE" \
"http://10.0.222.1:1337/bindPort"
Note: In Gluetun, if you provide VPN_PORT_FORWARDING=on, VPN_PORT_FORWARDING_PROVIDER=private internet access, and valid credentials, Gluetun will attempt this bind loop automatically if SERVER_NAME matches the gateway region.
Step 4: Rebuild Containers
/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container gluetun
/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container qbittorrent
5. Instant Rollback Plan (Back to OpenVPN)
If the WireGuard configuration ever fails or PIA's token exchange acts up, reverting to the proven OpenVPN setup takes under 30 seconds:
- In Unraid WebGUI $\rightarrow$ Docker $\rightarrow$ Edit
gluetun:- Set
VPN_SERVICE_PROVIDER=custom - Set
VPN_TYPE=openvpn - Set
OPENVPN_CUSTOM_CONFIG=/gluetun/custom.ovpn - Click Apply.
- Set
- Tunnel immediately restores to PIA Montreal with OpenVPN on port 23096.
Restart qbittorrent:
docker restart qbittorrent
6. Summary Comparison
| Dimension | OpenVPN (Active Today) | WireGuard (This Blueprint) |
|---|---|---|
| Throughput | ~400 Mbps (~48 MB/s) | ~700-1000 Mbps (~90-120 MB/s) |
| Configuration | Single XML template | Helper script + Custom WireGuard config |
| Port Forwarding | Fully automated by Gluetun | Requires API signature keepalive loop |
| Reliability | 100% resilient across reboots | Requires fresh token if key expires |
| Recommendation | Best for 24/7 "set and forget" | Best for maximum raw speed |