How to Setup qBittorrent with ProtonVPN & Port Forwarding on Debian 13 (Headless)

Jun 6, 2026 • 13 min read

thumbnail-showing-relation-between-torrent-secutory-and-server

Running a torrent client safely requires a solid VPN with port forwarding. In this guide, we will walk through setting up qBittorrent-nox (the headless version) alongside ProtonVPN CLI on a headless Debian 13 server or Proxmox LXC container .

We will also cover how to automate ProtonVPN’s dynamic port forwarding using a custom systemd service so your connection never drops and qBittorrent is always synced.

Note: You need a paid ProtonVPN plan to use P2P traffic; the free plan does not support it.

Tip: You can sometimes find cheap introductory offers (like 99 INR for the first month) via the ProtonVPN Android app .

Installing qBittorrent-nox

New LXC / minimal install? You may need to install sudo first: apt install sudo
(If you are running as root inside an LXC, you can skip sudo and run commands directly.)

First, we need to install the headless version of qBittorrent along with curl (needed later for the port-forwarding script). Run the following commands:

sudo apt update
sudo apt install qbittorrent-nox curl

We also need a proper UTF-8 locale, otherwise qBittorrent prints a warning in the logs. On Debian 13, the locales package is already installed by default, but we need to generate en_US.UTF-8 (it’s usually not generated by default):

sudo locale-gen en_US.UTF-8

What does this do? locale-gen generates locale data so the system knows about en_US.UTF-8. Without this, qBittorrent falls back to C.UTF-8 (which works but prints a warning every time).

Want to make UTF-8 the system default? Run this too:

sudo update-locale LANG=en_US.UTF-8

(Optional — qBittorrent works fine either way; this just cleans up system-wide locale warnings.)

First Run (Get Your Temporary Password)

qBittorrent generates a random admin password on its very first launch. You need to see this password before sending it to the background. Run it in foreground mode first with the --confirm-legal-notice flag to accept the legal notice:

qbittorrent-nox --confirm-legal-notice

You will see output like this:

******** Information ********
To control qBittorrent, access the WebUI at: http://localhost:8080
The WebUI administrator username is: admin
The WebUI administrator password was not set. A temporary password is provided for this session: rrkwEZLd8
You should set your own password in program preferences.

What’s happening? The --confirm-legal-notice flag tells qBittorrent you’ve read and accepted its terms. Without it, qBittorrent prints a legal notice every time. This flag also creates a [LegalNotice] Accepted=true entry in the config file so future runs don’t ask again.

Write down the temporary password (in this example: rrkwEZLd8), then press Ctrl+C to stop qBittorrent. We’ll restart it properly in the next step.

Run qBittorrent in the Background (Daemon Mode)

Now that the config file exists and the legal notice is accepted, start qBittorrent as a background daemon. You no longer need --confirm-legal-notice — it’s already saved in the config:

qbittorrent-nox --daemon

What does --daemon do? The -d / --daemon flag tells qBittorrent to fork into the background — it detaches from your terminal and keeps running even after you log out. No need for systemd or nohup! Every subsequent start from now on is just qbittorrent-nox --daemon.

Verify it’s running:

pgrep -a qbittorrent

You should see something like:

13596 qbittorrent-nox --daemon

Once running, you can access the WebUI from your browser at: http://<your-server-ip>:8080

Change the Admin Password

The temporary password changes every time qBittorrent restarts, so we need to set a permanent one. Since the daemon is running, we can use the WebUI API with curl:

# Log in with your temp password (replace 'rrkwEZLd8' with yours)
curl -c /tmp/qbit_cookie.txt -X POST -d "username=admin&password=rrkwEZLd8" http://localhost:8080/api/v2/auth/login

# Set a new password (change 'my_secure_password' to something strong!)
curl -b /tmp/qbit_cookie.txt -X POST -d "json=%7B%22web_ui_password%22%3A%22my_secure_password%22%7D" http://localhost:8080/api/v2/app/setPreferences

How this works: The first request logs in and stores a session cookie in /tmp/qbit_cookie.txt. The second request uses that cookie to send the new password to qBittorrent’s settings API. The %7B%22...%22%7D is URL-encoded JSON — it decodes to {"web_ui_password":"my_secure_password"}.

Important: Update the QBIT_PASS variable in the port-forwarding script later with this new password.

Optional: Auto-Start on Boot

If you want qBittorrent to start automatically when the server reboots, add the daemon command to /etc/rc.local:

sudo tee -a /etc/rc.local << 'EOF'
#!/bin/sh
qbittorrent-nox --daemon
exit 0
EOF
sudo chmod +x /etc/rc.local

What is rc.local? It’s a script that runs at the end of the boot process. Adding the daemon command here makes qBittorrent start automatically on every reboot. Make sure the file is executable (the chmod +x step).

Installing ProtonVPN CLI

Next, we need to install the official ProtonVPN command-line tool.

Before that, we need to make sure the LXC can create a VPN network. By default, LXC containers are not allowed to use the /dev/net/tun device. Get your LXC ID from the Proxmox host and edit its config:

nano /etc/pve/lxc/<lxc-id>.conf

Replace <lxc-id> with your actual LXC container ID.

Add this line at the end:

dev0: /dev/net/tun

Then restart the LXC from the Proxmox host:

pct reboot <lxc-id>

One way to install:

Warning: Installing proton-vpn-cli pulls in ~200+ packages (including NetworkManager, GTK libraries, GNOME keyring, etc.). On a minimal LXC this can take several minutes. This is normal — the ProtonVPN CLI has a lot of dependencies.

  1. Download the repository configuration package:

    wget https://repo.protonvpn.com/debian/dists/stable/main/binary-all/protonvpn-stable-release_1.0.8_all.deb
    
  2. Install the repository and update packages:

    sudo dpkg -i ./protonvpn-stable-release_1.0.8_all.deb
    sudo apt update
    

    You might get this dpkg error about missing gnupg:

    dpkg: dependency problems prevent configuration of protonvpn-stable-release:
     protonvpn-stable-release depends on gnupg | gnupg2; however:
      Package gnupg is not installed.
    

    To fix it, run:

    sudo apt install -f
    

    The -f (or --fix-broken) flag tells apt to download the missing dependencies (gnupg, dirmngr, etc.) and complete the installation.

    The .deb creates a source file at /etc/apt/sources.list.d/protonvpn-stable.sources. If it’s missing after apt install -f, just re-run sudo dpkg -i ./protonvpn-stable-release_1.0.8_all.deb to regenerate it.

  3. Update apt again (should now show repo.protonvpn.com in output):

    sudo apt update
    
  4. Install the CLI tool:

    sudo apt install proton-vpn-cli
    

    ⏱ This will take a while — NetworkManager, GNOME keyring, GTK libraries, and many other dependencies are being pulled in.

Another way to install (manual repo setup):

⚠️ Pick ONE method. If you already used the .deb method above, you already have the repo configured — skip this section. Running both methods creates duplicate apt source entries (one .sources file + one .list file) and wastes time on apt update.

If the .deb method above doesn’t work for you, you can add the ProtonVPN repo manually:

  1. Install the fetching utilities — make sure your container has the tools needed to download keys securely:
    sudo apt install -y curl gnupg ca-certificates
    
  2. Download the security key — manually download Proton’s GPG key and store it in your system’s keyring so apt trusts the packages:
    curl -fsSL https://repo.protonvpn.com/debian/public_key.asc | sudo gpg --dearmor -o /usr/share/keyrings/protonvpn-archive-keyring.gpg
    

    Note: If it asks to overwrite an existing file, press y and hit Enter.

  3. Add the repository manually — create the repository file:
    echo "deb [signed-by=/usr/share/keyrings/protonvpn-archive-keyring.gpg] https://repo.protonvpn.com/debian stable main" | sudo tee /etc/apt/sources.list.d/protonvpn.list
    
  4. Update apt — you should see repo.protonvpn.com in the output:
    sudo apt update
    
  5. Install the CLI:
    sudo apt install proton-vpn-cli
    

Crucial Step for Headless / Proxmox LXC Users

ProtonVPN CLI relies on D-Bus, which is designed for graphical desktop environments. In headless servers or Proxmox LXC containers, it will fail with:

dbus_fast.errors.InvalidAddressError: DBUS_SESSION_BUS_ADDRESS not set

To fix this, we need three things:

  1. Install dbus-x11 (provides dbus-launch — not installed by default on minimal Debian):

    sudo apt install dbus-x11
    
  2. Enable lingering — ensures background services continue running after logout:

    loginctl enable-linger $USER
    
  3. Start a D-Bus session — this must be done in every new SSH session before running any protonvpn commands:

    export $(dbus-launch)
    

Tip: To avoid typing export $(dbus-launch) every time you SSH in, add it to your ~/.bashrc. The if guard below prevents spawning a new D-Bus daemon on every shell (which would leave orphan processes):

echo 'if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then export $(dbus-launch); fi' >> ~/.bashrc

Sign in to ProtonVPN

Before you can connect or change settings, you need to log in with your ProtonVPN credentials:

Before running this: Make sure you have a D-Bus session running (see “Crucial Step” above). If you get a D-Bus error, run export $(dbus-launch) first and try again.

protonvpn signin <your_username>

You will be prompted for your password. It will not show up on the screen as you type.

Need a Plan? You need a paid ProtonVPN plan (not the free tier) for P2P and port forwarding.

Configuring ProtonVPN

Before connecting, we need to optimize our VPN configuration for P2P traffic. Note: These commands require you to be signed in first (see previous step).

  1. Enable Port Forwarding (Essential for P2P — without this, the later natpmpc setup won’t work):

    protonvpn config set port-forwarding on
    
  2. Enable Kill Switch — stops all traffic if the VPN drops:

    protonvpn config set kill-switch standard
    
  3. Enable IPv6 (Optional but recommended for full compatibility):

    protonvpn config set ipv6 on
    
  4. Verify your settings:

    protonvpn config list
    

    ⚠️ Troubleshooting: If this command hangs (no output for 10+ seconds), you are likely not properly signed in or D-Bus is not running. Press Ctrl+C, run export $(dbus-launch), verify you’re signed in with protonvpn status, then try again.

    For more details on the CLI, check the ProtonVPN Linux CLI documentation

Connect to a Server

Now connect to a P2P-friendly server:

protonvpn connect

Or specify a country (e.g., India):

protonvpn connect --country IN

Check the connection status:

protonvpn status

You should see Status: Connected. A new network interface proton0 should appear (check with ip a).

Automating Port Forwarding with natpmpc

Here lies the main issue: Even though we enabled port forwarding in the ProtonVPN CLI, it doesn’t automatically map the port to our local application on Debian. Furthermore, ProtonVPN assigns a random port every time you reconnect.

To fix this, we need a tool called natpmpc.

sudo apt install natpmpc

Note: Ensure your natpmpc version is 20230423-xxx or newer to avoid a known VPN mapping bug. Check with apt policy natpmpc. On Debian 13 (trixie), the available version is 20230423-1.2+b3 which works fine.

To manually request a port and keep it alive:

natpmpc -a 1 0 udp 60 -g 10.2.0.1 && natpmpc -a 1 0 tcp 60 -g 10.2.0.1

Finding the correct gateway IP: The script uses 10.2.0.1 — this is ProtonVPN’s default internal gateway. If it doesn’t work for you, find your actual gateway by running ip route after connecting to the VPN and looking for the proton0 interface’s default route.

The Systemd Automation Fix

We don’t want to run this manually, nor do we want to copy-paste the new port into qBittorrent every time the VPN reconnects. We will create a background service to handle this entirely.

  1. Create the Automation Script

    sudo nano /root/proton-port.sh 
    

    Paste the following code. Make sure to change your qBittorrent password in the QBIT_PASS variable!

    #!/bin/bash
    
    # --- CONFIGURATION ---
    QBIT_USER="admin"
    QBIT_PASS="your_password"   # CHANGE THIS to your WebUI password
    QBIT_URL="http://localhost:8080"
    GATEWAY="10.2.0.1"
    COOKIE_JAR="/tmp/qbit_cookie.txt"
    # ---------------------
    
    while true; do
        date
    
        # 1. Run natpmpc and grab the assigned port number
        PORT=$(natpmpc -a 1 0 udp 60 -g $GATEWAY | grep "Mapped public port" | awk '{print $4}')
    
        if [ -z "$PORT" ]; then
            echo "ERROR: Could not get a port from natpmpc"
            sleep 10
            continue
        fi
    
        # Run the TCP mapping as well (Proton requires both)
        natpmpc -a 1 0 tcp 60 -g $GATEWAY > /dev/null
    
        echo "ProtonVPN assigned Port: $PORT"
    
        # 2. Try to log into qBittorrent Web UI API
        # NOTE: We check the response body, not just curl's exit code.
        # curl returns exit 0 even on HTTP 403 (wrong password), so we
        # must check that the actual response is "Ok."
        LOGIN_RESULT=$(curl -s --connect-timeout 3 -X POST -d "username=${QBIT_USER}&password=${QBIT_PASS}" ${QBIT_URL}/api/v2/auth/login --cookie-jar $COOKIE_JAR)
    
        if [ "$LOGIN_RESULT" = "Ok." ]; then
    
            # 3. Send the new port to qBittorrent preferences
            curl -s -X POST -d "json=%7B%22listen_port%22%3A%22$PORT%22%7D" ${QBIT_URL}/api/v2/app/setPreferences --cookie $COOKIE_JAR > /dev/null
    
            echo "Successfully updated qBittorrent to use port $PORT"
            rm -f $COOKIE_JAR
        else
            echo "Notice: qBittorrent login failed (wrong password or WebUI unreachable). Check QBIT_PASS in the script."
        fi
    
        # Wait 45 seconds before renewing the VPN lease (Proton requires renewal every 60s)
        sleep 45
    done
    

    Note: The script needs curl (we already installed it in the qBittorrent step above).

    Save and close (CTRL+O, Enter, CTRL+X), then make it executable:

    sudo chmod +x /root/proton-port.sh
    
  2. Create the Systemd Service Now, let’s make it run securely in the background:

    sudo nano /etc/systemd/system/proton-port.service
    

    Paste the following:

    [Unit]
    Description=ProtonVPN NAT-PMP Port Forwarding Fixer
    After=network.target
    
    [Service]
    Type=simple
    ExecStart=/root/proton-port.sh
    Restart=always
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    

    Save and close.

  3. Enable and Start the Service

    sudo systemctl daemon-reload
    sudo systemctl enable proton-port.service
    sudo systemctl start proton-port.service
    

    You can view the live logs of this script to ensure it’s successfully talking to qBittorrent:

    sudo journalctl -u proton-port.service -f
    

Bind qBittorrent to the VPN Interface

The final and most important step to prevent IP leaks is to force qBittorrent to only use the VPN.

Accessing the WebUI remotely: The LXC/server is on your local network. If you can’t reach http://<server-ip>:8080 directly from your browser, use SSH port forwarding:

ssh -L 8080:localhost:8080 root@<your-server-ip>

Then open http://localhost:8080 in your local browser.

  1. Open the qBittorrent WebUI http://<server-ip>:8080.

  2. Navigate to Tools > Options > Advanced.

  3. Scroll down to Network interface and select proton0 from the drop-down. (This ensures that if the VPN drops, all downloads stop immediately).

    Under Optional IP address to bind to, select All addresses to allow both IPv4 and IPv6 (if you enabled IPv6 in ProtonVPN).

Final Checklist

  • ProtonVPN is running and connected (protonvpn status).

  • Port forwarding script is running successfully in the background.

  • qBittorrent is securely bound to proton0.

  • Torrents are downloading utilizing the active P2P port.


Troubleshooting & Debugging

If things aren’t working as expected, check these common issues:

  1. “Failed to connect to D-Bus” Errors

    If the protonvpn command throws DBus errors, you lost your session variables. Run this again before typing protonvpn commands:

    export $(dbus-launch)
    
  2. natpmpc returns an error or timeout

    • Verify your VPN is actually connected: protonvpn status
    • Ensure you are connected to a server that supports P2P (some free/basic servers do not).
    • Ensure port forwarding is actually toggled on: protonvpn config list (look for port_forwarding: on)
  3. qBittorrent isn’t updating to the new port

    Check the logs of your background service:

    sudo journalctl -u proton-port.service -n 50
    

    If it says Notice: qBittorrent login failed, check the following:

    • Did you update the QBIT_PASS variable in /root/proton-port.sh to match your actual WebUI password?
    • Did you change the default WebUI port? If so, update the QBIT_URL variable in the script.
    • Is qBittorrent running? Check with pgrep -a qbittorrent.
  4. I lost my qBittorrent Admin password!

    Quick reset (one-liner): Stop qBittorrent, delete the password hash from config, then restart:

    pkill qbittorrent-nox
    sleep 2
    sed -i '/^WebUI\\Password_PBKDF2=/d' ~/.config/qBittorrent/qBittorrent.conf
    qbittorrent-nox --daemon
    sleep 3
    timeout 5 qbittorrent-nox 2>&1 | grep "temporary password"
    

    What this does: pkill kills the daemon, sed removes the password line from the config file (which forces qBittorrent to generate a new one on next start), --daemon restarts it in the background. The timeout line runs it briefly in the foreground to capture the new temp password. After the first run, the legal notice is already saved in the config, so you never need --confirm-legal-notice again.

    Via manual edit: Open the config file:

    nano ~/.config/qBittorrent/qBittorrent.conf
    

    Find the line starting with WebUI\Password_PBKDF2=... and delete that entire line. Save and exit, then restart qBittorrent:

    pkill qbittorrent-nox
    qbittorrent-nox --daemon
    

    To see the new temporary password, run it in foreground briefly:

    timeout 5 qbittorrent-nox 2>&1 | grep "temporary password"