π¦ Parrot says: Smash that βοΈ or walk the plank, ye landlubber!
βββ β§ βββ
π Need help or wanna trade cursed tech tips over lava? Step forwardβ¦ Enter π₯HADESπ₯.
Ahoy! Privateerr is a lightweight Docker image that packages the official, unmodified, Private Internet Access (PIA) β¨ pia-foss/manual-connections β¨ scripts.
Those PIA scripts live in this repo as a submodule at docker/pia-manual-connections; Privateerr packages them into a small Alpine image, runs them, and makes the output easy to use and consume with automation.
And that's it! That's the whole trick: PIA's original scripts do the WireGuard work. Privateerr just gives them a clean container, friendly defaults, repeatable commands, and a small metadata file for Docker Compose setups.
Important
Privateerr is NOT a VPN client. It does not create or run a VPN tunnel. It generates a PIA WireGuard configuration file (wg0.conf) that you can hand to an actual VPN client.
There are already excellent VPN clients. One of the best container-friendly options is Gluetun. If what you really want is "get Gluetun working with PIA WireGuard," Privateerr may have you covered: generate the files, point Gluetun at them, and move on.
Clone the repo with submodules, copy the example environment file, then pass your PIA credentials inline:
β― git clone --recurse-submodules git@github.com:scottgigawatt/privateerr.git
β― cd privateerr
β― cp example.env .env
β― PIA_USER="p1234567" PIA_PASS="p0rtRoya1" PIA_PF="false" make run-privateerrYou don't even need to look at or update the .env file to get a fully functioning WireGuard configuration file in seconds! Now, the .env file is intentionally roomy, but the defaults are ready to go; just use inline variables to override anything in the .env file for that run.
When the command finishes, the main file you came for is here:
β― cat config/gluetun/wireguard/wg0.conf
[Interface]
Address = 10.10.10.10
PrivateKey = shiverMeTimbers123
DNS = 10.10.10.10
[Peer]
PersistentKeepalive = 25
PublicKey = yoHoHoPublicKey123
AllowedIPs = 0.0.0.0/0
Endpoint = 10.10.10.10:1234Privateerr also writes a tiny metadata file beside the WireGuard config:
β― cat config/gluetun/wireguard/privateerr.env
PIA_WG_SERVER_NAME=jolly-roger-401
PIA_WG_ENDPOINT_IP=10.10.10.10
PIA_WG_ENDPOINT_PORT=1234
PIA_REGION_ID=skull-island
PIA_REGION_NAME="Skull Island"
PIA_PORT_FORWARDING_SUPPORTED=true
PIA_GEOLOCATED_REGION=false| π File | π― What it is for |
|---|---|
config/gluetun/wireguard/wg0.conf |
The WireGuard config generated by PIA's scripts. Use this with Gluetun, WireGuard, or another VPN client that accepts WireGuard configs. |
config/gluetun/wireguard/privateerr.env |
A small helper file with the selected PIA server name, endpoint, region, and port-forwarding support metadata. |
Warning
Keep wg0.conf private. It contains VPN connection material.
Want a port-forwarding-capable PIA server? Change one variable:
β― PIA_USER="p1234567" PIA_PASS="p0rtRoya1" PIA_PF="true" make run-privateerrPIA port forwarding is usually awkward because the server choice matters after WireGuard is involved. Privateerr makes the handoff simple: with PIA_PF=true, it asks PIA for a port-forwarding-capable WireGuard endpoint, generates wg0.conf, figures out the matching PIA WireGuard server name, and writes that name into privateerr.env.
The metadata in privateerr.env is what lets a Docker Compose stack establish a fully functioning port forwarded VPN connection in one single step, in less than 1 minute. Privateerr writes the map, reads PIA_WG_SERVER_NAME, exports it as SERVER_NAMES, then Gluetun can start immediately with the right server information instead of making you run a second manual step after the VPN connection is established.
Tip
For the simplest use case, take wg0.conf and leave. For the powerful use case, pair Privateerr with Gluetun in Compose so config generation, server-name handoff, and VPN startup happen together.
For the wired-together version, see the included Compose file. For a larger real-world stack, see Plundarr.
Curious about every variable available in this project? Run:
β― make envIf you only care about the PIA settings, anchor the grep at the start of the line:
β― make env | grep ^PIA
PIA_BIN_HOME=/pia
PIA_VPN_PROTOCOL=wireguard
PIA_DISABLE_IPV6=yes
...If you want to see the Gluetun side:
β― make env | grep ^GLUETUN
GLUETUN_TAG=latest
GLUETUN_CONFIG_PATH=./config/gluetun
GLUETUN_WRAPPER_SCRIPT_PATH=./config/gluetun/scripts/gluetun-entrypoint-wrapper.sh
...These are the environment variables understood by PIA's own manual connection scripts. Privateerr did not invent them; it passes these values through to the scripts from pia-foss/manual-connections. Everything else in .env is scaffolding around that core: Docker image paths, Compose mounts, healthchecks, Gluetun handoff, logs, and test automation.
| π§© PIA script variable | π οΈ Privateerr .env variable |
What it configures |
|---|---|---|
PIA_USER |
PIA_USER |
Your PIA username. |
PIA_PASS |
PIA_PASS |
Your PIA password. |
DIP_TOKEN |
PIA_DIP_TOKEN |
Optional PIA dedicated IP token. |
PIA_DNS |
PIA_DNS |
Whether PIA should write DNS settings into the generated config. |
PIA_PF |
PIA_PF |
Whether to request a port-forwarding-capable PIA region. |
PIA_CONNECT |
PIA_CONNECT |
Whether the PIA script should connect after generating config. Privateerr sets this to false so it only writes files. |
PIA_CONF_PATH |
PIA_CONF_PATH |
Where the WireGuard config file is written inside the container. |
MAX_LATENCY |
Not set by default | Optional latency threshold used by PIA's region selection. |
AUTOCONNECT |
PIA_AUTOCONNECT |
Whether PIA should pick the lowest-latency region automatically. |
PREFERRED_REGION |
Not set by default | Optional PIA region ID when you want to choose a specific region. |
VPN_PROTOCOL |
PIA_VPN_PROTOCOL |
Which PIA protocol to use. Privateerr uses wireguard. |
DISABLE_IPV6 |
PIA_DISABLE_IPV6 |
Whether PIA's scripts should disable IPv6 behavior. |
The included docker-compose.yml is intentionally variable-heavy. That keeps the file reusable, but it can make it hard to read raw.
Use these commands when you want Docker Compose to show you what it sees:
| π§ Command | π What it prints |
|---|---|
make print-config |
The project Compose file with comments removed, but variables still visible. |
make config |
The fully rendered Docker Compose model with variables resolved from .env. |
So if you want to inspect the template, run:
β― make print-configIf you want to see the actual Compose configuration Docker will run:
β― make config| βοΈ Command | β Use it when |
|---|---|
make run-privateerr |
You only want fresh wg0.conf and privateerr.env. |
make up |
You want the full Privateerr + Gluetun Compose stack. |
make down |
You want to stop and remove the stack. |
make logs |
You want to inspect container output. |
make print-config |
You want the Compose template without comments. |
make config |
You want the fully rendered Compose model. |
make reset-config |
You want to restore the checked-in example config files. |
make clean |
You want to stop the stack and restore example config files. |
Tip
More build, test, and release details live in Advanced Usage.
The image builds from Alpine for a small footprint. Alpine is not a distro PIA lists as officially confirmed for the manual scripts, so this project validates the container path separately before publishing images.
Published Privateerr images support linux/amd64, linux/arm64, and linux/arm/v7.
Images are published to both GHCR and Docker Hub:
| π¦ Registry | π·οΈ Image |
|---|---|
| GHCR | ghcr.io/scottgigawatt/privateerr:latest |
| Docker Hub | scottgigawatt/privateerr:latest |
The Docker Hub overview is generated from docs/DOCKERHUB_README.md, which keeps Docker Hub focused on pulling the image and understanding the basic use case. The full project docs stay here in the GitHub README.
Privateerr keeps the build deck intentionally locked down:
- GitHub Actions are pinned to full commit SHAs.
- Alpine build bases are pinned to versioned image digests.
- Renovate opens update PRs for pinned actions, Docker digests, Compose images, and submodules.
- Pre-commit, CodeQL, OpenSSF Scorecard, and Trivy guard the repo and image workflow.
- Release images are built on
main, scanned with Trivy before publish, attested, and mirrored from GHCR to Docker Hub with digest preservation.
This means a new main build does not silently float to a newer Alpine base just because Alpine published one. Renovate has to raise the flag, CI has to pass, and the update has to merge before the next published image uses that new base.
Privateerr is licensed under Apache 2.0; see LICENSE. The bundled PIA manual connection scripts are licensed under MIT by PIA; see their license.
| π Guide | π§ Purpose |
|---|---|
| π€ Contributing | How to offer changes without sinking the dinghy. |
| βοΈ Code of Conduct | Pirate manners, but useful. |
| π Security Policy | How to report leaky hulls without shouting secrets across the harbor. |
______
.-' `-.
/ \
| |
|, .-. .-. ,|
| )(_o/ \o_)( |
|/ /\ \|
(_ ^^ _)
\__|IIIIII|__/
| \IIIIII/ |
\ /
`--------`
β οΈ Privateerr β οΈ
Adventure Awaits, Treasure Beckons!
ποΈ Cast yer pull requests ashore, or send a message in a bottle.
The sea calls, the code answers. Fair winds and full containers, matey! ππ΄ββ οΈ