adxglock/qb-port-sync

By adxglock

Updated 8 months ago

Automatically syncs the qBittorrent listening port with the VPN-assigned forwarded port from Gluetun

Image
Networking
Monitoring & observability
0

4.2K

adxglock/qb-port-sync repository overview

🌀 qb-port-sync

Automatically sync qBittorrent’s listening port with Gluetun’s VPN-forwarded port

Docker Image Version Docker Pulls GitHub Workflow Status License


🧭 Overview

qb-port-sync is a lightweight Python-based sidecar container that automatically updates your qBittorrent listening port to match the VPN-assigned forwarded port from Gluetun.

Perfect for setups where qBittorrent runs through Gluetun (network_mode: service:gluetun) with VPN providers that support port forwarding such as ProtonVPN, Mullvad, and others.


🚀 Features

✅ Syncs qBittorrent’s listening port with the latest Gluetun-forwarded port
✅ Detects Gluetun VPN reconnections and updates automatically
✅ Optional firewall monitor — restarts qBittorrent if it becomes firewalled
✅ Optional webhook notifications (Discord, Slack, ntfy, Gotify, etc.)
✅ Built for Docker — minimal footprint (~15MB)
✅ Works with WireGuard and OpenVPN connections


🧩 Example Docker Compose Setup

services:
  gluetun:
    image: qmcgaw/gluetun:latest
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    ports:
      - 8888:8888/tcp # HTTP proxy
      - 8388:8388/tcp # Shadowsocks
      - 8388:8388/udp # Shadowsocks
      - 8000:8000 # required for Gluetun API access
      # Other services
      # qBittorrent
      - 9092:9092  # qBittorrent Web UI
      #- 6881:6881 #torrenting port  Not needed if using qBittorrent with network_mode: "service:gluetun"
      #- 6881:6881/udp #torrenting port  Mpt needed if using qBittorrent with network_mode: "service:gluetun"
      # Other containers that are being routed via the container where you still want access the resources like web ui on local network
    networks:
      internal:
      web:
    volumes:
      - /configs/gluetun:/gluetun
    environment:
      # I specify the PUID and PGID but this is optional (default is 1000,1000), description from Wiki "User ID/Group ID to run as non root and for ownership of files written"
      - PUID=1001
      - PGID=100
      - UMASK=002
      - TZ=America/New_York
      # See https://github.com/qdm12/gluetun-wiki/tree/main/setup#setup
      #- VPN_SERVICE_PROVIDER=custom
      - VPN_SERVICE_PROVIDER=protonvpn
      - VPN_TYPE=wireguard
      # OpenVPN:
      #- OPENVPN_USER=
      #- OPENVPN_PASSWORD=
      # Wireguard:
      #- WIREGUARD_PUBLIC_KEY="XXXXXXXXXXXXXXXX"
      #- WIREGUARD_PRIVATE_KEY="XXXXXXXXXXXXXXXXXX"
      - WIREGUARD_PRIVATE_KEY=XXXXXXXXXXXXXXXXXX
      - SERVER_COUNTRIES=United States
      - PORT_FORWARD_ONLY=on
      #- WIREGUARD_ADDRESSES=10.2.0.2/32
      #- VPN_ENDPOINT_IP=146.70.202.130 # "Endpoint" under [Peer] in WG Config
      #- VPN_ENDPOINT_PORT=51820 # should be the default 51820 but can confirm by seeing the port after IP in "Endpoint"
      #- VPN_DNS_ADDRESS=DNS = 10.2.0.1 # "DNS" under [Interface] in WG Config
      - VPN_PORT_FORWARDING=on
      - VPN_PORT_FORWARDING_PROVIDER=protonvpn
      # Server list updater
      # See https://github.com/qdm12/gluetun-wiki/blob/main/setup/servers.md#update-the-vpn-servers-list
      - UPDATER_PERIOD=24h
      - HTTP_CONTROL_SERVER_USER=admin
      - HTTP_CONTROL_SERVER_PASSWORD=password
    labels:
      - "com.centurylinklabs.watchtower.enable=True"
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    network_mode: "service:gluetun"
    depends_on:
      gluetun:
        condition: service_healthy
    environment:
      - PUID=1001
      - PGID=100
      - UMASK=002
      - TZ=America/New_York
      - WEBUI_PORT=9092
      #- TORRENTING_PORT=6881
    volumes:
      - /configs/qbittorrent:/config
      - /downloads:/downloads #optional
      - /media:/media #optional for uploading
    labels:
      - "com.centurylinklabs.watchtower.enable=True"
    healthcheck:
      test: curl --fail http://ifconfig.me/ || exit 1
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
      #test: "ifconfig | grep -q eth0"
      #interval: 1m
      #timeout: 30s
      #retries: 0
    restart: unless-stopped

  qb-port-sync:
    #build: ./qb-port-sync
    image: adxglock/qb-port-sync:latest
    container_name: qb-port-sync
    network_mode: "service:gluetun"
    depends_on:
      gluetun:
        condition: service_healthy
      qbittorrent:
        condition: service_healthy
    environment:
      - GLUETUN_API=http://localhost:8000/v1/openvpn/portforwarded
      - GLUETUN_USER=admin
      - GLUETUN_PASS=password
      - QBITTORRENT_HOST=http://localhost:9092
      - QBITTORRENT_USER=admin
      - QBITTORRENT_PASS=adminadmin
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - "com.centurylinklabs.watchtower.enable=True"
#    healthcheck:  #HEALTHCHECK ADDED INTO DOCKERFILE
#      test: curl --fail http://ifconfig.me/ || exit 1
#      interval: 30s
#      timeout: 10s
#      retries: 3
#      start_period: 60s
    restart: unless-stopped
networks:
  internal:
    external: true
    name: internal
  web:
    external: true
    name: web

📝 Tip:
Make sure qbittorrent and qb-port-sync share the same network namespace as Gluetun by using
network_mode: "service:gluetun".
This allows API access via localhost inside the shared namespace.


⚙️ Environment Variables

VariableDefaultDescription
GLUETUN_APIhttp://localhost:8000/v1/openvpn/portforwardedGluetun API endpoint
GLUETUN_USERadminGluetun control server username
GLUETUN_PASSpasswordGluetun control server password
QBITTORRENT_HOSThttp://localhost:9092qBittorrent WebUI base URL
QBITTORRENT_USERadminqBittorrent WebUI username
QBITTORRENT_PASSadminadminqBittorrent WebUI password
CHECK_INTERVAL300Poll interval in seconds
MONITOR_FIREWALLtrueIf enabled, restarts qBittorrent when firewalled
QBT_CONTAINER_NAMEqbittorrentContainer name to restart
WEBHOOK_URL(optional)Webhook URL for notifications

🔍 How It Works

  1. Polls the Gluetun control API (/v1/openvpn/portforwarded) every few minutes
  2. Reads the latest VPN-forwarded port number
  3. Logs into qBittorrent’s WebUI via API
  4. Updates its listen_port dynamically
  5. (Optional) Checks for “firewalled” state and restarts the qBittorrent container
  6. (Optional) Sends webhook notifications when changes or restarts occur

🧪 Manual Run Example

docker run --rm \
  -e GLUETUN_API=http://localhost:8000/v1/openvpn/portforwarded \
  -e GLUETUN_USER=admin \
  -e GLUETUN_PASS=password \
  -e QBITTORRENT_HOST=http://localhost:9092 \
  -e QBITTORRENT_USER=admin \
  -e QBITTORRENT_PASS=yourpass \
  -v /var/run/docker.sock:/var/run/docker.sock \
  adxglock/qb-port-sync:latest

🧑‍💻 Maintainer

Author: ADXGlock
GitHub: ADXGlock
DockerHub: adxglock


🛡️ License

This project is licensed under the MIT License — see the LICENSE file for details.


⭐ Support

If you find this project helpful:


💬 Example webhook notification (ntfy)

If you set WEBHOOK_URL=https://ntfy.sh/qb-port-sync, you’ll receive:

🔁 Updated qBittorrent port to 42311
⚠️ qBittorrent is firewalled. Restarting container...

By ADXGlock For Gluetun + qBittorrent power users.

Tag summary

Content type

Image

Digest

sha256:fe45a4b38

Size

47.3 MB

Last updated

8 months ago

docker pull adxglock/qb-port-sync