Docudjeex
Media & Seedbox

Qbittorrent

Install qBittorrent with Gluetun and ProtonVPN to download torrents securely behind a VPN on your self-hosted server.

To safely download your favorite media, we'll build a system using:

  • Qbittorrent as the BitTorrent client
  • Proton VPN Plus, 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
  • Qbittorrent port update to automatically update the VPN port (which changes regularly).
  • The VueTorrent mod for a modern and intuitive UI.

Here’s the system we’ll set up:

Configuration

  • /
    • srv
      • docker
        • seedbox
          • qbittorrent
            • config
          • gluetun
          • compose.yaml
          • .env
    • medialinked to Jellyfin and Qbittorrent
      • downloadsgeneric downloads, selected in settings
      • moviesused for downloading movies
      • tvseriesused for downloading TV shows

Create the media folders

If not already done, create the downloads folder under /media:

Terminal
mkdir -P /media/downloads

Deploy the stack

Open Dockge, click on compose, and name the stack seedbox. Paste the following config:

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:
      - /srv/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:
      - /srv/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: Add the Watchtower label in each container to automate updates
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:

Terminal
sudo docker run --rm qmcgaw/gluetun genkey

Note the key, then create the /srv/docker/gluetun folder:

Terminal
sudo mkdir /srv/docker/gluetun

Create the config.toml file:

Terminal
sudo nano /srv/docker/gluetun/config.toml

Enter:

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 Ctrl+O, then Enter to save, and Ctrl+X to exit.

Set your environment variables

In Dockge, fill in the variables in .env:

.env
PUID=
GUID=
UI_PORT=
PR_KEY=
GSP_KEY= # the key you generated and entered in config.toml
ID=
PW=

Detailed info:

VariableDescriptionExample
PUIDUser ID (id yourusername)1000
GUIDGroup ID (id yourusername)1000
UI_PORTPort for accessing the web UI5695
PR_KEYPrivate key from ProtonbuKsjNHLyzKMM1qYnzOy4s7SHfly
GSP_KEYKey you generated for port updateMnBa47MeVmk7xiv
IDQbittorrent UI login usernameuser
PWQbittorrent UI passwordpassword

Done !

Deployment

Deploy the container

Once done, deploy the container.

Startup logs will show a temporary password for admin user. See logs in Dockge to retrieve it, or type this command in a terminal:
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).

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

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 or Authentik. Otherwise, don’t expose it with SWAG. Use a VPN like Wireguard instead.

To start downloads from outside your home, without a VPN, you can expose the Qbittorrent web UI.

We assume you have the subdomain seedbox.mydomain.com with a CNAME pointing to mydomain.com in DNS zone. And that port 443 on your router is forwarded to your server in NAT rules, unless you’re using Cloudflare Zero Trust.

Add Gluetun's network to SWAG

In Dockge, edit the SWAG compose file and add Gluetun’s network:

compose.yaml
---
services:
  swag:
    container_name: # ...
    # ...
    networks:
      # ...
      - seedbox

networks:
  # ...
  seedbox:
    name: seedbox_default
    external: true

Click "Deploy" and wait for SWAG to fully initialize.

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.

Terminal-free tip: use File Browser Quantum to edit files instead of using the terminal.
Terminal
sudo nano /srv/docker/swag/config/nginx/proxy-confs/seedbox.subdomain.conf

Paste the following config (check the port):

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 Ctrl+O, then Enter to save, and 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's forward-auth check directly to seedbox.subdomain.conf, the same way as the TinyAuth guide:

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 /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;

    }
}
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. If you run into an error, double-check SWAG's compose file still has that network attached.
✨ You can secure this app with 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.

And that’s it! You now have a ready-to-use media center.

Contributor:Djeex
Copyright © 2026