qBittorrent + ProtonVPN (WireGuard) on QNAP, fully routed through VPN with automatic port forwarding Mod, DNS set to Proton (10.2.0.1), and a startup that avoids the classic qB WebUI Unauthorized issue on QNAP Container Station.
- qBittorrent behind Gluetun (ProtonVPN WireGuard)
- Automatic port forwarding GSP Mod (keeps qB's listen port synced with Gluetun)
- WebUI exposed on host
:8081(QNAP already uses:8080) - Proton DNS 10.2.0.1 and WG address 10.2.0.2/32 (no Cloudflare)
- Safe first-boot WebUI options to avoid Unauthorized
- Clean structure, consistent paths:
- Config:
${CONFIG_ROOT}/qBittorrent/ - Downloads:
/Downloads(host${DOWNLOADS_ROOT}) - Categories & watched folders for new .torrent
- Config:
Tested on QNAP HS-264 + 2.5" SSD
- Upload this repo to your NAS, e.g.
/share/CACHEDEV3_DATA/SSD2TB/stacks/qbt-proton-qnap - Copy
envExampleto.envand edit it:WIREGUARD_PRIVATE_KEY→ get it from https://account.protonvpn.com/GLUETUN_API_KEY→ generate withdocker run --rm qmcgaw/gluetun genkeyand copy the output, Or write any long random string- Adjust
CONFIG_ROOTandDOWNLOADS_ROOTif your pool is notCACHEDEV3_DATAor notSSD2TB - Adjust
SERVER_COUNTRIES/SERVER_CITIESto any ProtonVPN server location (with P2P/port‑forwarding).
Important: The
/Downloadsfolder must be empty or non‑existent for the first start. Move your files after the stack is up, then Force recheck in qBittorrent.
- Allow SSH in QNAP Control Panel, the script works fine with PuTTY
cd /share/CACHEDEV3_DATA/SSD2TB/stacks/qbt-proton-qnap
sh scripts/install.sh-
WebUI is at:
http://<your-nas-ip>:8081(useradmin) -
If qBittorrent changed the password automatically, to print it:
docker logs qbittorrent 2>&1 | grep -A1 "WebUI administrator username is" | tail -n 1 | awk '{print $NF}'
-
Mandatory : Log in once
-
(Optional) Move your own torrents into
/Downloads/...and your BT_backup into intoAppData/qbt-proton/qBittorrent
- Mandatory: Run this script once after initial login or when you restored torrents
cd /share/CACHEDEV3_DATA/SSD2TB/stacks/qbt-proton-qnap
sh scripts/fix_after_login.sh- Wait. Wait. 1 to 5 minutes.
What it does:
- Stops qB & Gluetun cleanly (with waits)
- Patches `.fastresume` from `/downloads` → `/Downloads`
- Ensures `/Downloads/Incomplete` and `/Downloads/Torrents` exist
- Starts Gluetun (waits until **healthy**), then qB
- Reads the **forwarded port** from Gluetun
- Applies qB settings **via WebUI API** (not file edits) to avoid *Unauthorized*:
- `bypass_local_auth=true`
- `web_ui_host_header_validation=false`
- `web_ui_csrf_protection_enabled=false`
- whitelist `127.0.0.1/32,::1/128`
- save path `/Downloads`, temp `/Downloads/Incomplete`
- listen port = forwarded port
- Restarts qB and verifies
- Some may need to reboot the NAS or stop then run again qbt-proton-qnap in Container Station
- If you have put your torrents in the correct paths, Force recheck them in Qbittorrent WebUI.
- log in and set a new password (qBittorrent → Settings → WebUI )
- Security note : after everything works and after you changed the admin password, consider enabling tighter options in qB WebUI
- New .torrent added in /Downloads/Torrents are automatically added to qBittorrent. (Set qBittorrent → Settings → Downloads → Default Save Path → Copy .torrent files for finished downloads to: /Downloads/torrentsfiles)
- Use categories to move your torrents files, e.g. create categories like "FILM_To_Move" and and set the NAS to automatically relocate the folder contents.
# VPN public IP
docker run --rm --network=container:gluetun alpine:3.20 sh -c 'apk add -q --no-progress curl >/dev/null && curl -s https://ipinfo.io'
# DNS leak test (script default)
docker run --rm --network=container:gluetun alpine:3.20 sh -c ' apk add -q --no-progress curl wget >/dev/null && curl -s https://raw.githubusercontent.com/macvk/dnsleaktest/master/dnsleaktest.sh -o /tmp/d && chmod +x /tmp/d && /tmp/d'
# Forwarded port from Gluetun
docker exec gluetun sh -lc 'cat /tmp/gluetun/forwarded_port || curl -s http://localhost:8000/v1/openvpn/portforwarded'qbt-proton-qnap/
├─ envExample # copy to .env and edit
├─ docker-compose.yml
├─ scripts/
│ ├─ install.sh
│ └─ fix_after_login.sh
└─ qBittorrent/
├─ qBittorrent.conf
├─ categories.json
└─ watched_folders.json
- All qB config files end up in
${CONFIG_ROOT}/qBittorrent/ - Downloads are in
${DOWNLOADS_ROOT}mounted at/Downloads
- We use Proton DNS 10.2.0.1 and set
DOT=offso DNS is not routed via Gluetun’s Unbound. - The Gluetun control server (port 8000 inside the container) is protected by an API key defined in
.env(GLUETUN_API_KEY) and written to/gluetun/auth/config.toml. - The qB GSP Mod uses the same API key (
GSP_GTN_API_KEY) to read the forwarded port and keep qB in sync.
- Country/City:
France / Paris - WebUI: host
:8081 - qB: anonymous mode disabled, encryption Allow, UPnP off
- ulimits:
nofilesoft32768, hard65536
You can change SERVER_COUNTRIES / SERVER_CITIES in .env. See Proton’s list for other P2P/port‑forwarding cities.
-
Unauthorized on WebUI
Always runscripts/fix_after_login.shonce after first login. It applies the API settings that prevent the 401 with LSIO + Mod. -
Port 8081 busy
ChangeWEBUI_HOST_PORTin.env, thendocker compose down && docker compose up -d. -
Paths mismatch or torrents “missing files”
Ensure you moved your files only after the first start. Then select torrents and Force recheck. -
Forwarded port is 0
Wait until Gluetun is healthy. Checkdocker logs gluetun | grep -i forward. -
You messed up with qBittorrent settings
Stop the app qbt-proton-qnap in Container Station
Copy qBittorrent.conf fromstacks\qbt-proton-qnap\qBittorrentand paste/replace intoAppData\qbt-proton\qBittorrent
Re-run the fix script :cd /share/CACHEDEV3_DATA/SSD2TB/stacks/qbt-proton-qnap sh scripts/fix_after_login.sh -
If qBittorrent changed the password automatically, to print it:
docker logs qbittorrent 2>&1 | grep -A1 "WebUI administrator username is" | tail -n 1 | awk '{print $NF}'
This starter is intentionally permissive for WebUI to avoid “Unauthorized” on QNAP.
After everything works and after you changed the admin password, consider enabling tighter options in qB WebUI:
- Enable Host header validation
- Enable CSRF protection
- Restrict Auth subnet whitelist to your LAN
- Optionally close WebUI exposure and use a reverse proxy with Auth
Edit through the WebUI (recommended, settings there can break the WebUI identification) or adjust and re-run the fix script with your desired values.
- Based on: https://github.com/torrentsec/qbittorrent-protonvpn-docker
- Thanks https://github.com/torrentsec
- Thanks https://github.com/t-anc/GSP-Qbittorent-Gluetun-sync-port-mod
- Thanks https://github.com/qdm12/gluetun
This project is licensed under the MIT License – see the LICENSE file for details.