added gluetun with qBittorrent and pyload-ng
VPN kill switch via shared network namespace, TorGuard WireGuard through gluetun's custom provider. compose.yaml is the maintained version; compose.personal.yaml is the variant actually running, kept as-is. Verified against gluetun's own settings summary: SOCKSPROXY/SOCKSPROXY_PORT are not real variables and the SOCKS5 server never starts; HTTPPROXY_PORT is deprecated; pyload-ng listens on 8000, not 8880. .gitignore: ignore wg*.conf and *.ovpn everywhere, because add gluetun/compose.yaml gluetun/compose.personal.yaml gluetun/.env.example gluetun/README.md .gitignore/init/** would otherwise commit a WireGuard private key dropped in a service's init/ dir. Un-ignore compose.personal.yaml narrowly, so compose.override.yaml stays out.
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
# gluetun + qBittorrent + pyload-ng
|
||||
|
||||
A VPN container with two download clients living inside its network stack. The
|
||||
point is the **kill switch**: qBittorrent and pyload-ng have no network of their
|
||||
own, so if the tunnel drops they lose connectivity entirely rather than falling
|
||||
back to your real address.
|
||||
|
||||
Also exposes gluetun's HTTP and SOCKS5 proxies, so other machines and browser
|
||||
extensions (SwitchyOmega and friends) can route through the same tunnel without
|
||||
running a VPN client themselves.
|
||||
|
||||
Configured for **TorGuard WireGuard** via gluetun's `custom` provider.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
$EDITOR .env # TEMPDISK is the one you must set
|
||||
|
||||
# Get a WireGuard config from TorGuard's config generator, save it as
|
||||
# wg0.conf NEXT TO THIS FILE. It is git-ignored. Do not put it in init/.
|
||||
cp ~/Downloads/torguard-wg.conf ./wg0.conf
|
||||
|
||||
docker compose up -d
|
||||
docker compose logs -f gluetun # watch for the public IP line
|
||||
```
|
||||
|
||||
Verify the tunnel is actually carrying traffic before you seed anything:
|
||||
|
||||
```bash
|
||||
docker compose exec gluetun wget -qO- https://ipinfo.io/ip # should be TorGuard's
|
||||
curl -x http://localhost:9998 https://ipinfo.io/ip # same, via the HTTP proxy
|
||||
```
|
||||
|
||||
## Ports
|
||||
|
||||
Every port is published on the **gluetun** service, because the other two
|
||||
containers have no network namespace of their own.
|
||||
|
||||
| Host | Container | Purpose |
|
||||
|------|-----------|---------|
|
||||
| 9880 | 9880 | qBittorrent web UI |
|
||||
| 8880 | 8000 | pyload-ng web UI |
|
||||
| 9980 | 1080 | SOCKS5 proxy |
|
||||
| 9998 | 8888 | HTTP proxy |
|
||||
|
||||
In `compose.personal.yaml` two of these differ: pyload is published `8880:8880`
|
||||
against a container that listens on 8000, and the SOCKS5 server isn't running at
|
||||
all. See the gotchas.
|
||||
|
||||
## Two compose files
|
||||
|
||||
**[`compose.yaml`](compose.yaml)** is the maintained one and what
|
||||
`docker compose up` uses.
|
||||
|
||||
**[`compose.personal.yaml`](compose.personal.yaml)** is the older variant still
|
||||
running on my own box. It works and its HTTP proxy is in daily use, so it's kept
|
||||
as-is rather than tidied — but it has the quirks listed under gotchas below.
|
||||
Compose won't load it unless you name it:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.personal.yaml up -d
|
||||
```
|
||||
|
||||
What the maintained file changes:
|
||||
|
||||
| Change | Why |
|
||||
|--------|-----|
|
||||
| `SOCKS5_ENABLED` / `SOCKS5_LISTENING_ADDRESS` | `SOCKSPROXY*` are not gluetun variables; the SOCKS5 server never starts |
|
||||
| `HTTPPROXY_LISTENING_ADDRESS` | `HTTPPROXY_PORT` works but logs a deprecation warning |
|
||||
| `8880:8000` | pyload-ng listens on 8000; its port isn't settable by env var |
|
||||
| `HTTPPROXY_USER` / `SOCKS5_USER` + passwords | opt-in proxy authentication, off unless set in `.env` |
|
||||
| `qmcgaw/gluetun:v3.41.3` | pinned; this container holds the tunnel |
|
||||
| `${PUID:-1000}`, `${TEMPDISK:?…}` | defaults where sensible, loud failure where a value can't be guessed |
|
||||
| `condition: service_healthy` | gluetun ships a healthcheck; wait for the tunnel instead of racing it |
|
||||
| `lscr.io/linuxserver/qbittorrent` | matches pyload-ng's registry; same image |
|
||||
|
||||
Same design, same ports, same volumes.
|
||||
|
||||
## Other VPN providers
|
||||
|
||||
Nothing here is TorGuard-specific except `wg0.conf`. gluetun natively supports
|
||||
around twenty providers, and for most of them you delete the file mount and set
|
||||
two or three variables instead:
|
||||
|
||||
```yaml
|
||||
- VPN_SERVICE_PROVIDER=mullvad
|
||||
- VPN_TYPE=wireguard
|
||||
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
|
||||
- WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
|
||||
- SERVER_CITIES=Amsterdam
|
||||
```
|
||||
|
||||
**Native WireGuard** — the easy path: AirVPN, FastestVPN, IVPN, Mullvad,
|
||||
NordVPN, ProtonVPN, Surfshark, Windscribe.
|
||||
|
||||
**Native OpenVPN** — every supported provider, usually just `OPENVPN_USER` and
|
||||
`OPENVPN_PASSWORD`: the list above plus Cyberghost, ExpressVPN, Giganews,
|
||||
HideMyAss, IPVanish, Privado, Private Internet Access, PrivateVPN, PureVPN,
|
||||
SlickVPN, TorGuard, VPNSecure, VPN Unlimited, VyprVPN.
|
||||
|
||||
**WireGuard via the custom provider** — Cyberghost, Private Internet Access,
|
||||
PrivateVPN, PureVPN, TorGuard, VPN Unlimited and VyprVPN have no native
|
||||
WireGuard support in gluetun, so they take the `wg0.conf` route this stack uses.
|
||||
|
||||
That last line is why this stack looks the way it does: **TorGuard *is* a
|
||||
built-in provider (`VPN_SERVICE_PROVIDER=torguard`), but only for OpenVPN.**
|
||||
Wanting TorGuard *and* WireGuard is precisely the case gluetun's own
|
||||
documentation sends to the custom provider. It isn't a workaround — it's the
|
||||
documented path.
|
||||
|
||||
Switching provider changes nothing else: the kill switch, the shared network
|
||||
namespace and both proxies work the same way regardless of who terminates the
|
||||
tunnel. Check your provider's page in
|
||||
[the gluetun wiki](https://github.com/qdm12/gluetun-wiki) for its exact
|
||||
variables.
|
||||
|
||||
## Why it looks like this
|
||||
|
||||
**`network_mode: "service:gluetun"` is the whole design.** qBittorrent and
|
||||
pyload-ng don't get their own network stack — they share gluetun's. That's what
|
||||
makes the kill switch absolute: there is no route out except the tunnel, so a
|
||||
misconfiguration fails closed rather than leaking. The cost is that those
|
||||
containers **cannot declare their own `ports:`**; Docker refuses it. Every port
|
||||
they need is published on gluetun instead, which is why the list above looks
|
||||
like it belongs to the wrong service.
|
||||
|
||||
**gluetun's firewall is on by default.** Confirmed in its startup summary
|
||||
(`Firewall settings: Enabled: yes`) — you don't have to configure a kill switch,
|
||||
you have to avoid breaking the one that's already there.
|
||||
|
||||
**`VPN_SERVICE_PROVIDER=custom` plus a mounted `wg0.conf`.** TorGuard isn't one
|
||||
of gluetun's built-in providers, so rather than translating their config into
|
||||
`WIREGUARD_PRIVATE_KEY` / `VPN_ENDPOINT_IP` environment variables, the whole
|
||||
config file is mounted at `/gluetun/wireguard/wg0.conf` and gluetun parses it.
|
||||
Verified working: gluetun reads the endpoint, interface address and keys
|
||||
straight out of that file. It also keeps the private key out of `docker inspect`
|
||||
and out of your shell history, which the environment-variable approach does not.
|
||||
|
||||
**`:latest` on all three images**, which is upstream's habit rather than a
|
||||
decision. Worth pinning gluetun in particular — it's the one holding the tunnel.
|
||||
|
||||
## Security: the proxies are unauthenticated by default
|
||||
|
||||
Both proxies ship with no credentials, so **anyone who can reach ports 9998 or
|
||||
9980 can route traffic through your VPN account**. On a trusted LAN that may be
|
||||
exactly what you want — it's what makes them useful from a browser extension on
|
||||
another machine. It is not something to expose beyond that, and not something to
|
||||
forget you did.
|
||||
|
||||
`compose.yaml` has the variables wired already; set both in `.env` and
|
||||
authentication switches on for both proxies at once:
|
||||
|
||||
```bash
|
||||
PROXY_USER=someone
|
||||
PROXY_PASSWORD=a-long-random-string
|
||||
```
|
||||
|
||||
Leave them unset and behaviour is unchanged. gluetun needs the user *and*
|
||||
password together — one alone does nothing.
|
||||
|
||||
Never port-forward these from your router. An open proxy on the public internet
|
||||
is found within hours, and everything that follows is attributed to your VPN
|
||||
account.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`SOCKSPROXY` is not a gluetun variable.** Neither is `SOCKSPROXY_PORT`.
|
||||
gluetun ignores both silently — no warning — so the SOCKS5 server never
|
||||
starts and its published port maps to nothing. Its own settings summary is
|
||||
the proof: `SOCKS5 proxy server settings: Enabled: no`. The real names are
|
||||
`SOCKS5_ENABLED=on` and `SOCKS5_LISTENING_ADDRESS=:1080`. Fixed in
|
||||
`compose.yaml`; still present in `compose.personal.yaml`, whose HTTP proxy
|
||||
works fine and whose SOCKS port does not.
|
||||
- **`HTTPPROXY_PORT` is deprecated but does work.** gluetun logs `WARN You are
|
||||
using the old environment variable HTTPPROXY_PORT, please consider changing
|
||||
it to HTTPPROXY_LISTENING_ADDRESS`, then brings the proxy up on 8888 anyway.
|
||||
- **pyload-ng listens on 8000.** The LinuxServer image's web UI port is 8000
|
||||
and is *not* settable by environment variable — only inside pyload's own
|
||||
config in `./pyloadconfig`. So a `8880:8880` mapping reaches nothing unless
|
||||
you already changed it there; `compose.yaml` publishes `8880:8000` instead.
|
||||
(Click'n'Load on 9666 isn't published at all.)
|
||||
- **`wg0.conf` must exist before the first `up`.** It's a bind mount of a
|
||||
*file*; if the path is missing, Docker creates a **directory** there, gluetun
|
||||
finds no config, and the error doesn't mention the mount. Same failure mode as
|
||||
[overleaf's init script](../overleaf/README.md#running-it-on-openmediavault).
|
||||
- **Never put `wg0.conf` in `init/`.** This repo's `.gitignore` un-ignores
|
||||
`*/init/**` so that runtime config fragments get committed — which for a
|
||||
WireGuard config means publishing your private key. `wg*.conf` and `*.ovpn`
|
||||
are now explicitly ignored everywhere as a guard, but the habit is the real
|
||||
protection.
|
||||
- **Restarting gluetun cuts the other two off.** They share its namespace, so
|
||||
when it goes away their network does too, and `depends_on` doesn't bring them
|
||||
back. Restart the whole stack, not one service.
|
||||
- **`TORRENTING_PORT=6881` is not published**, so incoming peer connections
|
||||
don't arrive and you're limited to outgoing ones. Fixing that properly needs
|
||||
port forwarding from TorGuard, then publishing the matching port on gluetun.
|
||||
- **In `compose.personal.yaml` the variables have no defaults.** `${PUID}`,
|
||||
`${PGID}`, `${TZ}` and `${TEMPDISK}` are bare, so Compose interpolates empty
|
||||
strings and starts the containers anyway. `compose.yaml` uses
|
||||
`${PUID:-1000}` and `${TEMPDISK:?…}`, which aborts with a readable message
|
||||
instead.
|
||||
- **Check for leaks after any change**, not just at setup. `docker compose exec
|
||||
gluetun wget -qO- https://ipinfo.io/ip` is the ten-second version.
|
||||
|
||||
## Exposing it
|
||||
|
||||
**Don't.** This stack has no business being reachable from the internet: two
|
||||
admin UIs with weak-by-default auth and two unauthenticated open proxies.
|
||||
|
||||
If you need the UIs remotely, reach them over your own VPN or a
|
||||
[Cloudflare Tunnel](../cloudflare-tunnel/) with
|
||||
[Authentik forward auth](../docs/authentik-sso.md#forward-auth--putting-an-app-with-no-sso-behind-authentik)
|
||||
in front — and leave the proxy ports off it entirely.
|
||||
|
||||
## Links
|
||||
|
||||
- gluetun (now at the passteque org): <https://github.com/passteque/gluetun>
|
||||
- gluetun wiki: <https://github.com/qdm12/gluetun-wiki>
|
||||
- TorGuard WireGuard + gluetun guide:
|
||||
<https://www.torguard.net/support/articles/TorGuard-Software-Features/torguard-wireguard-gluetun-docker-setup.php>
|
||||
- qBittorrent image: <https://docs.linuxserver.io/images/docker-qbittorrent/>
|
||||
- pyload-ng image: <https://docs.linuxserver.io/images/docker-pyload-ng/>
|
||||
Reference in New Issue
Block a user