Generate standalone WireGuard® configuration files for Private Internet Access — without installing their app.
PIA hands out OpenVPN profiles, but getting a WireGuard config out of them means running their desktop app. That leaves out routers, a Raspberry Pi, pfSense, OPNsense, a NAS, or anything else that speaks WireGuard and nothing else.
This tool does what their app does at the protocol level: generate a Curve25519 key pair on your
machine, register the public half with a PIA server, and write out a .conf you can import anywhere.
The private key never leaves the computer you ran it on.
- Real WireGuard configs. Plain
.conffiles that any WireGuard client will import — routers, phones, servers, containers. - Keys generated locally. Curve25519 in the app itself. You do not need WireGuard,
wg, or Go installed to produce a configuration. - Every PIA region, live. Fetched from PIA's server list at launch, filtered to regions that actually offer WireGuard. Pin the ones you use.
- DNS you choose. PIA's in-tunnel resolvers — standard, streaming-optimised, or MACE ad-blocking — or any resolver of your own.
- Import by QR. Scan straight into the WireGuard app on a phone.
- Nothing phones home. No analytics, no update checks, no fonts from a CDN. The only hosts it ever contacts are Private Internet Access's own.
Download one file for your platform from Releases — each is 2–4 MB, there is no
installer, and nothing is written outside the folder you put it in. There is no .app bundle, .msi
or .deb: it is a single executable.
Windows — pia-wireguard-generator-win_x64.exe
Double-click it.
Windows SmartScreen will warn that the publisher is unknown, because the binary is not code-signed —
signing certificates cost money this project does not have. Choose More info → Run anyway, or
verify the SHA-256 against SHA256SUMS on the release first:
Get-FileHash .\pia-wireguard-generator-win_x64.exe -Algorithm SHA256Requires the WebView2 runtime, which Windows 11 includes and Windows 10 gets with Microsoft Edge — in practice it is already there.
macOS — pia-wireguard-generator-mac_universal (Intel and Apple Silicon)
It is a plain binary rather than an app bundle, so it needs the executable bit, and macOS will quarantine anything downloaded from the internet that is not notarized:
chmod +x pia-wireguard-generator-mac_universal
xattr -d com.apple.quarantine pia-wireguard-generator-mac_universal
./pia-wireguard-generator-mac_universalWithout the xattr line you get "cannot be opened because the developer cannot be verified".
Nothing else is needed — macOS provides the webview and curl.
Linux — pia-wireguard-generator-linux_x64 (also arm64, armhf)
chmod +x pia-wireguard-generator-linux_x64
./pia-wireguard-generator-linux_x64Needs a GTK webview, which most desktop installs already have. If the app exits telling you so:
sudo apt install libwebkit2gtk-4.1-0 # Debian/Ubuntu — or libwebkit2gtk-4.0-37 on older releases
sudo dnf install webkit2gtk4.1 # Fedora
sudo pacman -S webkit2gtk-4.1 # ArchThen: sign in with your PIA username (it starts with p), pick a region and a DNS resolver, hit
Generate, and either Save .conf file or scan the QR code from the WireGuard app on your
phone. Import the file into your router or client and you are done.
Tip
PersistentKeepalive = 25 is already set, which is what you want behind NAT. If your client
expects the interface address without a prefix, drop the /32.
Note
Preferences live in a .storage folder created next to the executable, so keep it somewhere you
can write — your home directory or a USB stick is fine, C:\Program Files is not. Deleting that
folder resets the app completely.
Important
The app uses curl for every network request. Windows 10 1803+, macOS, and most Linux distributions ship it. If it is missing the app tells you on startup rather than failing later.
PIA's servers drop a WireGuard registration after some hours without a handshake, and the account token lasts a day, so a tunnel on a router that reboots or loses its WAN for a while comes back as Not Established until a fresh configuration is pasted in. For UniFi gateways (UCG Ultra, UDM, UDR) there is a headless companion that does the whole round trip on a timer:
cp examples/pia-unifi-sync.example.json pia-unifi-sync.json # name your VPN Clients and regions
export PIA_USERNAME=p1234567 PIA_PASSWORD=… UNIFI_API_KEY=… # or UNIFI_USERNAME + UNIFI_PASSWORD
node scripts/pia-unifi-sync.mjs --config pia-unifi-sync.json --dry-runIt signs in to PIA with the same pinned, validated code path as the app, registers a new key pair
for each tunnel, and rewrites only the key, endpoint and address fields of the matching WireGuard
VPN Client in the console — routing and everything else you set up stay as they are. The
console's self-signed certificate is pinned from a file you export, never ignored. Needs Node 20+
and curl on any always-on machine that can reach both the internet and the gateway; systemd units
are in examples/systemd/, and the details are in
docs/wiki/features/UniFi_Automation.md.
This tool handles a VPN credential and writes a private key to disk, so it is worth being precise about what it does.
Your password never touches a command line. Requests are made with curl, because the webview
cannot call PIA's API directly — their endpoints send no CORS headers. But the command executed is
always the fixed string curl -q --config -, and the entire request travels to it on standard
input as a curl configuration file. Nothing you type is ever parsed by a shell, so a password
containing `, $( ), &, %VAR% or a quote is just a password. It is also never visible in
ps output, in shell history, or to process-monitoring software.
Certificates are verified, including PIA's internal ones. The key-registration endpoint runs on
PIA's own certificate authority rather than a public one. This app bundles that CA and pins to it,
completing the handshake against the server's certificate name — the same approach PIA's published
manual-connections scripts use. If verification fails, the request fails: no key is registered and
no configuration is written. Earlier versions of this tool passed -k here, which accepted any
certificate at all.
On Windows, where curl verifies through Schannel, pinned requests additionally pass
--ssl-revoke-best-effort, because PIA's authority publishes no revocation endpoint for Schannel to
consult. That tolerates revocation data being missing; it does not skip verification, and a revoked
certificate is still rejected.
Your session token is not stored unless you ask. "Stay signed in" is off by default. When it is off the token lives in memory and is gone when you quit. When it is on, it is stored with a recorded expiry and revalidated on the next launch. Signing out erases it.
Saved configurations are owner-only. The .conf contains your private key, so it is written with
0600 permissions. If the platform refuses, the app tells you rather than assuming.
Nothing is sent anywhere else. A Content-Security-Policy of default-src 'none' means the app
window cannot load a remote script, style, font, or image even if one were added by mistake. A test
in the suite fails the build if any non-PIA URL appears in the source.
Found a problem? See SECURITY.md. Please do not open a public issue for it.
git clone https://github.com/vonRoX/PIA-WireGuard-Generator.git
cd PIA-WireGuard-Generator
npm install
npm run build # binaries for every platform, in dist/npm run build runs neu update first — without it the Neutralino client library is missing and
neu build writes nothing while still reporting success. The build ends in
scripts/verify-build.mjs, which fails loudly if no real binary was produced.
To run it during development:
npm start # launches the app
npm test # unit and integration tests
npm run test:e2e # browser tests (needs: npx playwright install chromium)
npm run lintThe test suite runs entirely offline. Command-injection payloads are pushed through a real shell and a real curl; certificate pinning is checked against a local TLS server with a throwaway CA, including that a certificate from the wrong authority is rejected.
you ──▶ sign in ──▶ POST /api/client/v2/token ▶ session token
│
├─▶ GET serverlist.piaservers.net/…/v6 ▶ regions with WireGuard
│
└─▶ generate X25519 keypair (locally)
│
└─▶ GET https://<cn>:1337/addKey ▶ peer IP, server key, endpoint
--cacert <PIA CA> │
--connect-to <cn>:1337:<ip>:1337
└─▶ .conf written to disk
The application is Neutralinojs, so a release binary is a few megabytes rather than a bundled
browser. More detail lives in docs/wiki/, including the
security model.
- Not affiliated with Private Internet Access. This is a community tool. It is not endorsed by, associated with, or supported by PIA.
- WireGuard is a registered trademark of Jason A. Donenfeld.
- Your credentials go to
privateinternetaccess.comand nowhere else. That is a design property with tests behind it, not just a promise — but you are welcome to read the source and check.
MIT. Bundled third-party components keep their own licences — see the end of that file.

