Created
December 10, 2025 14:59
-
-
Save klinquist/446c207670620adc4d383bedfeed6dc8 to your computer and use it in GitHub Desktop.
ProtonVPN+Synology+qBittorrent. Run natpmpc every minute, update qbittorrent config if port changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # Keep qBittorrent's incoming port in sync with ProtonVPN NAT-PMP. | |
| # Runs natpmpc to fetch the current mapped port, updates qBittorrent.conf | |
| # if it changed, and restarts the container so the new port is applied. | |
| set -eu | |
| # ---- configurable bits ------------------------------------------------------ | |
| # NAT-PMP gateway (ProtonVPN typically uses 10.2.0.1) | |
| GATEWAY="${GATEWAY:-10.2.0.1}" | |
| # Path to natpmpc binary | |
| NATPMPC_BIN="${NATPMPC_BIN:-/opt/bin/natpmpc}" | |
| # qBittorrent config paths (adjust if your config lives elsewhere) | |
| CONFIG_DIR="${CONFIG_DIR:-/volume1/docker/qbittorrent/qBittorrent}" | |
| QBT_CONF="${QBT_CONF:-${CONFIG_DIR}/qBittorrent.conf}" | |
| # Container controls (override these if you use docker compose or another name) | |
| CONTAINER="${CONTAINER:-qbittorrent}" | |
| STOP_CMD=${STOP_CMD:-"docker stop ${CONTAINER}"} | |
| START_CMD=${START_CMD:-"docker start ${CONTAINER}"} | |
| # ---- functions -------------------------------------------------------------- | |
| fatal() { | |
| echo "[$(date '+%Y-%m-%d %H:%M | |
| require_cmd() { | |
| command -v "$1" >/dev/null 2>&1 || fatal "missing command: $1" | |
| } | |
| update_port_in_conf() { | |
| conf_file="$1" | |
| new_port="$2" | |
| tmp_file="$(mktemp "${conf_file}.XXXX")" | |
| trap 'rm -f "$tmp_file"' EXIT | |
| awk -v p="$new_port" ' | |
| BEGIN {s=0; c=0} | |
| /^Session\\Port=/ {print "Session\\Port=" p; s=1; next} | |
| /^Connection\\PortRangeMin=/ {print "Connection\\PortRangeMin=" p; c=1; next} | |
| {print} | |
| END { | |
| if (!s) print "Session\\Port=" p | |
| if (!c) print "Connection\\PortRangeMin=" p | |
| } | |
| ' "$conf_file" >"$tmp_file" | |
| mv "$tmp_file" "$conf_file" | |
| trap - EXIT | |
| } | |
| # ---- main ------------------------------------------------------------------- | |
| require_cmd awk | |
| require_cmd grep | |
| require_cmd sed | |
| require_cmd mktemp | |
| require_cmd sh | |
| require_cmd date | |
| require_cmd cp | |
| require_cmd "$NATPMPC_BIN" | |
| [ -f "$QBT_CONF" ] || fatal "qBittorrent config not found at $QBT_CONF" | |
| # Ask NAT-PMP for a fresh mapping (UDP then TCP, TCP is the one we care about). | |
| map_output="$("$NATPMPC_BIN" -a 1 0 udp 60 -g "$GATEWAY" 2>&1 && \ | |
| "$NATPMPC_BIN" -a 1 0 tcp 60 -g "$GATEWAY" 2>&1)" | |
| new_port="$(echo "$map_output" | awk '/Mapped public port .* protocol TCP/ {print $4; exit}')" | |
| case "$new_port" in | |
| ''|*[!0-9]*) fatal "could not parse mapped TCP port from natpmpc output";; | |
| esac | |
| current_port="$( | |
| awk -F= ' | |
| /^Session\\Port=/ {sp=$2} | |
| /^Connection\\PortRangeMin=/ {cp=$2} | |
| END { | |
| if (sp) print sp; else if (cp) print cp | |
| } | |
| ' "$QBT_CONF" | tail -n 1 | |
| )" | |
| if [ "$new_port" = "$current_port" ]; then | |
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Port unchanged ($new_port); nothing to do." | |
| exit 0 | |
| fi | |
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Port change detected: $current_port -> $new_port" | |
| # Stop container before editing the config. | |
| if [ -n "$STOP_CMD" ]; then | |
| echo "Stopping qBittorrent container..." | |
| sh -c "$STOP_CMD" >/dev/null | |
| fi | |
| # Backup config before modifying. | |
| backup="${QBT_CONF}.bak.$(date +%Y%m%d%H%M%S)" | |
| cp "$QBT_CONF" "$backup" | |
| echo "Config backed up to $backup" | |
| update_port_in_conf "$QBT_CONF" "$new_port" | |
| # Start container again. | |
| if [ -n "$START_CMD" ]; then | |
| echo "Starting qBittorrent container..." | |
| sh -c "$START_CMD" >/dev/null | |
| fi | |
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] Updated Session\\Port to $new_port" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment