--- title: Qbittorrent description: Install qBittorrent with Gluetun and ProtonVPN to download torrents securely behind a VPN on your self-hosted server. --- :ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60} ![Picture](/img/serveex/qbit-vue.jpeg) To safely download your favorite media, we'll build a system using: - [Qbittorrent](https://github.com/linuxserver/docker-qbittorrent) as the BitTorrent client - [Proton VPN Plus](https://protonvpn.com/torrenting), a VPN to secure your traffic. You need a subscription (promos available) to access the BitTorrent protocol. You can also use another VPN as long as it supports BitTorrent. - [Gluetun](https://github.com/qdm12/gluetun) - [Qbittorrent port update](https://codeberg.org/TechnoSam/qbittorrent-gluetun-port-update) to automatically update the VPN port (which changes regularly). - The [VueTorrent](https://github.com/gabe565/linuxserver-mod-vuetorrent) mod for a modern and intuitive UI. Here’s the system we’ll set up: ![Picture](/img/serveex/qbit.svg) ## Configuration ::file-tree --- tree: /: - docker: - seedbox: - qbittorrent: - config/ - gluetun/ - compose.yaml - .env - "media # linked to Jellyfin and Qbittorrent": - "downloads/ # generic downloads, selected in settings" - "movies/ # used for downloading movies" - "tvseries/ # used for downloading TV shows" --- :: ::steps{level="3"} ### Create the media folders If not already done, create the `downloads` folder under `/media`: ```bash [Terminal] mkdir -P /media/downloads ``` ### Deploy the stack Open Dockge, click on `compose`, and name the stack `seedbox`. Paste the following config: ```yaml [compose.yaml] --- services: qbit: image: ghcr.io/linuxserver/qbittorrent:libtorrentv1 container_name: qbittorrent restart: unless-stopped network_mode: service:gluetun mem_limit: 4g environment: - DOCKER_MODS=ghcr.io/gabe565/linuxserver-mod-vuetorrent|ghcr.io/t-anc/gsp-qbittorent-gluetun-sync-port-mod:main - TZ=Europe/Paris - PUID=${PUID} - PGID=${GUID} - WEBUI_PORT=${UI_PORT} - GSP_GTN_API_KEY=${GSP_KEY} - GSP_QBT_USERNAME=${ID} - GSP_QBT_PASSWORD=${PW} volumes: - /docker/seedbox/qbittorrent/config:/config - /media:/media depends_on: - gluetun gluetun: image: qmcgaw/gluetun:v3.41.3 container_name: gluetun restart: unless-stopped mem_limit: 4g volumes: - /docker/gluetun/config.toml:/gluetun/auth/config.toml:ro devices: - /dev/net/tun:/dev/net/tun ports: - ${UI_PORT}:5695 # Port de la web-ui - 8000:8000 # Port de controle de Gluetun cap_add: - NET_ADMIN environment: - TZ=Europe/Paris - VPN_SERVICE_PROVIDER=protonvpn - VPN_PORT_FORWARDING=on - VPN_PORT_FORWARDING_PROVIDER=protonvpn - VPN_TYPE=wireguard - WIREGUARD_PRIVATE_KEY=${PR_KEY} - SERVER_COUNTRIES=France - PORT_FORWARD_ONLY=on ``` ::tip{icon=""} ✨ __Tip:__ Add the Watchtower label in each container to automate updates ```yaml [compose.yaml] --- services: qbittorrent: #... labels: - com.centurylinklabs.watchtower.enable=true gluetun: #... labels: - com.centurylinklabs.watchtower.enable=true ``` :: ### Configure the download port update Before editing the `.env` in Dockge, let's configure the download port update. Proton and most VPNs rotate the forwarding port, which must be communicated to Qbittorrent. We’ve added the mod `ghcr.io/t-anc/gsp-qbittorent-gluetun-sync-port-mod` to the container. We now need to allow the mod to fetch info from Gluetun, which only allows encrypted communication via its API. Open a terminal to generate the authentication key: ```bash [Terminal] sudo docker run --rm qmcgaw/gluetun genkey ``` Note the key, then create the `/docker/gluetun` folder: ```bash [Terminal] sudo mkdir /docker/gluetun ``` Create the `config.toml` file: ```bash [Terminal] sudo nano /docker/gluetun/config.toml ``` Enter: ```toml [config.toml] [[roles]] name = "t-anc/GSP-Qbittorent-Gluetun-sync-port-mod" routes = ["GET /v1/portforward"] auth = "apikey" apikey = "your_key_here" # key you just generated ``` Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit. ### Set your environment variables In Dockge, fill in the variables in `.env`: ```properties [.env] PUID= GUID= UI_PORT= PR_KEY= GSP_KEY= # the key you generated and entered in config.toml ID= PW= ``` Detailed info: | Variable | Description | Example | |------------|-------------|---------| | `PUID` | User ID (`id yourusername`) | `1000` | | `GUID` | Group ID (`id yourusername`) | `1000` | | `UI_PORT` | Port for accessing the web UI | `5695` | | `PR_KEY` | Private key from Proton | `buKsjNHLyzKMM1qYnzOy4s7SHfly` | | `GSP_KEY` | Key you generated for port update | `MnBa47MeVmk7xiv` | | `ID` | Qbittorrent UI login username | `user` | | `PW` | Qbittorrent UI password | `password` | ### Done ! :: ## Deployment ::steps{level="3"} ### Deploy the container Once done, deploy the container. ::note **Startup logs will show a temporary password for `admin` user.** See logs in [Dockge](/serveex/core/docker/#dockge) to retrieve it, or type this command in a terminal: ```bash [Terminal] docker logs qbittorrent 2>&1 | grep -i "temporary password" ``` :: ### Log in and secure your account Login at `http://server-ip:5695` (or the port you set). ::caution __If login fails:__ check your firewall rules. :: Change your username and password in the "webui" settings. ### Done ! :: You're done! In Qbittorrent settings, under "Downloads", set `/media/downloads` as the default folder. When adding a download, remember to select the proper directory so Jellyfin can sync correctly (`/media/movies` or `/media/tvseries`). You can also automate this with categories and folders. ## Exposing the Web UI ::warning Qbittorrent does not support multi-factor authentication. Exposing it to the internet may put your system at risk. Only do this if you use MFA via [TinyAuth](/serveex/security/tinyauth) or [Authentik](/serveex/advanced/authentik/). Otherwise, don’t expose it with SWAG. Use a VPN like [Wireguard](/serveex/security/wireguard) instead. :: To start downloads from outside your home, without a VPN, you can expose the Qbittorrent web UI. ::note We assume you have the subdomain `seedbox.mydomain.com` with a `CNAME` pointing to `mydomain.com` in [DNS zone](/general/networking/dns). And that port `443` on your router is forwarded to your server in [NAT rules](/general/networking/nat), unless you’re using Cloudflare Zero Trust. :: ::steps{level="3"} ### Add Gluetun's network to SWAG In Dockge, edit the SWAG compose file and add Gluetun’s network: ```yaml [compose.yaml] --- services: swag: container_name: # ... # ... networks: # ... - seedbox networks: # ... seedbox: name: seedbox_default external: true ``` Click "Deploy" and wait for SWAG to fully initialize. ::note We assume the network name is `seedbox_default`. You can confirm by checking the SWAG dashboard at http://server-ip:81. :: ### Create the subdomain.conf file Now create/edit `seedbox.subdomain.conf`. ::tip{icon=""} ✨ __Terminal-free tip:__ use [File Browser Quantum](/serveex/files/file-browser-quantum) to edit files instead of using the terminal. :: ```bash [Terminal] sudo nano /docker/swag/config/nginx/proxy-confs/seedbox.subdomain.conf ``` Paste the following config (check the port): ```nginx [seedbox.subdomain.conf] ## Version 2023/12/19 server { listen 443 ssl; listen [::]:443 ssl; server_name seedbox.*; include /config/nginx/ssl.conf; client_max_body_size 0; #if ($lan-ip = yes) { set $geo-whitelist yes; } #if ($geo-whitelist = no) { return 404; } if ($geo-blacklist = no) { return 404; } # enable for ldap auth (requires ldap-location.conf in the location block) #include /config/nginx/ldap-server.conf; # enable for Authelia (requires authelia-location.conf in the location block) #include /config/nginx/authelia-server.conf; # enable for Authentik (requires authentik-location.conf in the location block) #include /config/nginx/authentik-server.conf; location / { # enable the next two lines for http auth #auth_basic "Restricted"; #auth_basic_user_file /config/nginx/.htpasswd; # enable for ldap auth (requires ldap-server.conf in the server block) #include /config/nginx/ldap-location.conf; # enable for Authelia (requires authelia-server.conf in the server block) #include /config/nginx/authelia-location.conf; # enable for Authentik (requires authentik-server.conf in the server block) #include /config/nginx/authentik-location.conf; include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app gluetun; set $upstream_port 5555; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit. ### Done ! :: Wait a few minutes, then go to `https://seedbox.mydomain.com`. You should land on the Qbittorrent interface. ## Protecting Qbittorrent with TinyAuth Add [TinyAuth](/serveex/security/tinyauth)'s forward-auth check directly to `seedbox.subdomain.conf`, the same way as [the TinyAuth guide](/serveex/security/tinyauth#protecting-an-app-via-reverse-proxy): ```nginx [seedbox.subdomain.conf]{26-38,41-42} ## Version 2023/12/19 server { listen 443 ssl; listen [::]:443 ssl; server_name seedbox.*; include /config/nginx/ssl.conf; client_max_body_size 0; #if ($lan-ip = yes) { set $geo-whitelist yes; } #if ($geo-whitelist = no) { return 404; } if ($geo-blacklist = no) { return 404; } # enable for ldap auth (requires ldap-location.conf in the location block) #include /config/nginx/ldap-server.conf; # enable for Authelia (requires authelia-location.conf in the location block) #include /config/nginx/authelia-server.conf; # enable for Authentik (requires authentik-location.conf in the location block) #include /config/nginx/authentik-server.conf; location /tinyauth { internal; proxy_pass http://tinyauth:3000/api/auth/nginx; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Uri $request_uri; } location @tinyauth_login { return 302 https://tinyauth.mydomain.com/login?redirect_uri=$scheme://$http_host$request_uri; } location / { auth_request /tinyauth; error_page 401 = @tinyauth_login; # enable the next two lines for http auth #auth_basic "Restricted"; #auth_basic_user_file /config/nginx/.htpasswd; # enable for ldap auth (requires ldap-server.conf in the server block) #include /config/nginx/ldap-location.conf; # enable for Authelia (requires authelia-server.conf in the server block) #include /config/nginx/authelia-location.conf; # enable for Authentik (requires authentik-server.conf in the server block) #include /config/nginx/authentik-location.conf; include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app gluetun; set $upstream_port 5555; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` ::note The `location /tinyauth` block runs inside SWAG's own container, so SWAG needs to be on TinyAuth's Docker network to reach it by name (`tinyauth` here). This should already be set up from [exposing TinyAuth itself](/serveex/security/tinyauth#exposing-tinyauth-with-swag). If you run into an error, double-check SWAG's compose file still has that network attached. :: ::tip{icon=""} ✨ You can secure this app with [Authentik](/serveex/advanced/authentik) instead of TinyAuth by uncommenting the `authentik-server.conf` and `authentik-location.conf` lines. Don’t forget to [create an app and provider in Authentik](/serveex/advanced/authentik#protecting-an-app-via-reverse-proxy). :: And that’s it! You now have a ready-to-use media center. ![Picture](/img/serveex/seed.svg)