---
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}
# Qbittorrent

::note
🎯 __Goals:__

- Install and configure Qbittorrent
- Securely connect to the BitTorrent network using Gluetun and Proton VPN
::

![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
Folder structure

```text [Directory tree]
root
├── 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
```

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

```bash [Terminal]
mkdir -P /media/downloads
```

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
```
::

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.

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` |

## Deployment
Once done, deploy the container.

::warning

**Startup logs will show a temporary password for `admin` user**
::

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.

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 [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.
::

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.
::

Now create/edit `seedbox.subdomain.conf`.

::tip{icon=""}
✨ __Terminal-free tip:__ use [File Browser](/serveex/files/file-browser) 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;

    }
}
```

::tip
✨ You can secure this app with [TinyAuth](/serveex/security/tinyauth) and [Pocket ID](/serveex/security/pocket-id) using the reverse-proxy pattern from the TinyAuth guide, or with Authentik 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).
::

Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit.

Wait a few minutes, then go to `https://seedbox.mydomain.com`. You should land on the Qbittorrent interface.

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

![Picture](/img/serveex/seed.svg)
