port-sync

Keeps qBittorrent's port synced to Proton VPN

Proton VPN hands out a new forwarded port on most reconnects, and peers can't reach qBittorrent until it listens there. port-sync reads the port from Proton's log every 15 seconds and sets it through qBittorrent's Web UI.

Install, in a normal PowerShell window

irm https://raw.githubusercontent.com/Real-Fruit-Snacks/port-sync/main/install.ps1 | iex

Run it as the Windows account you torrent with. It makes a Web UI password and tells you where to paste it in qBittorrent, does one test sync, then registers a task that starts at sign-in. One UAC prompt, for the task.

Proton VPN forwarded port

qBittorrent listening port

In syncOut of sync

2026-10-26 09:02:17 [port-sync] listening port already correct: 48213

Proton VPN's forwarded port and qBittorrent's listening port, both 48213. A simulation: the 15 s wait is shortened to 1.5 s.

Why it exists

On Linux the Proton app writes the forwarded port to a file. On Windows it only writes it to its own log, as Port pair 48213->48213. Without port-sync, every reconnect means opening qBittorrent's settings by hand, or a slow torrent until you notice.

port-sync assumes the VPN and qBittorrent are already set up so torrent traffic can't leave outside the tunnel. The setup guide is that setup, step by step: the kill switch, binding qBittorrent to the adapter, port forwarding and a leak test.

The loop

In: Proton VPN's client-logs.txt

Port pair 48213->48213

The newest line wins. A later Disconnected clears it.

port-sync, every 15 s

  1. Checks the ProtonVPN adapter is up.
  2. Reads the newest Port pair line.
  3. Logs in to the Web UI and reads qBittorrent's port: when Proton's changes, and once a minute anyway.
  4. If they differ, sets it, reads it back, logs out.

Out: qBittorrent's Web UI on 127.0.0.1

POST /api/v2/app/setPreferences
{"listen_port":48213,
 "random_port":false,"upnp":false}

The same call turns off qBittorrent's random port and UPnP.

The Web UI password is generated by the installer, encrypted with DPAPI to your Windows account, and only ever sent to 127.0.0.1. After a rejected login, port-sync waits for the password file to change, or 30 minutes, because qBittorrent bans an address after 5 failed logins in a row.

What sync.log tells you

One line per change, in %LOCALAPPDATA%\port-sync\sync.log. A condition that repeats is written once.

LineMeans
listening port updated: 49160 -> 48213Fixed. qBittorrent now listens on Proton's port.
listening port already correct: 48213Checked. Nothing to change.
VPN adapter 'ProtonVPN' is not up - waitingWaiting for the VPN to connect.
no forwarded port in the Proton VPN log yet - waitingWaiting. Port forwarding is off, or a Proton update changed the log line. Nothing else breaks.
qBittorrent Web UI not reachable (not running?) - will retryRetrying. Start qBittorrent; the port lands within 60 s.
Web UI login rejected (wrong password, or address banned)Stopped for 30 minutes, or until the password file changes. Fix the password, then quit and reopen qBittorrent.
can't decrypt …\qbit-pass.txtWrong account. The file opens only for the Windows account that made it. Re-run the installer as the account you torrent with.

Day to day

Watch it

Get-Content "$env:LOCALAPPDATA\port-sync\sync.log" -Wait -Tail 20

One cycle: exit 0 if qBittorrent has the forwarded port, 1 if not

powershell -NoProfile -ExecutionPolicy Bypass -File "$env:LOCALAPPDATA\port-sync\port-sync.ps1" -Once

Restart the task

Stop-ScheduledTask port-sync; Start-ScheduledTask port-sync

Uninstall: removes the task and %LOCALAPPDATA%\port-sync; qBittorrent keeps its last port

& ([scriptblock]::Create((irm https://raw.githubusercontent.com/Real-Fruit-Snacks/port-sync/main/install.ps1))) -Uninstall

Coming from proton-qbit-port-sync 1.x: run the same install line. It keeps your password file, replaces the old task, and moves to %LOCALAPPDATA%\port-sync.

Parameters

The defaults fit the setup guide. To change one for the task, add it to the task's arguments in an admin PowerShell. Re-running the installer resets them.

$t = Get-ScheduledTask port-sync
$t.Actions[0].Arguments += " -Adapter 'ProtonVPN TUN'"
Set-ScheduledTask -InputObject $t
Stop-ScheduledTask port-sync; Start-ScheduledTask port-sync
ParameterDefault
-QbitUrlhttp://127.0.0.1:8080
-QbitUseradmin
-ProtonLogs%LOCALAPPDATA%\Proton\Proton VPN\Logs
-AdapterProtonVPN. ProtonVPN TUN for OpenVPN, '' to skip the check
-StateDir%LOCALAPPDATA%\port-sync: the password file and sync.log
-IntervalSec15: how often to read the Proton log
-RecheckSec60: how often to re-read qBittorrent's port when Proton's hasn't changed

Tested on

  • Ubuntu 24.04, PowerShell 7.4.6, qbittorrent-nox 4.6.3: the Pester suite, including the sync against a real Web UI, with simulated Proton log lines.
  • Windows Server 2022 (GitHub's windows-latest), Windows PowerShell 5.1 and PowerShell 7: the unit tests, and the installer against a real Task Scheduler.
  • Not tested: the Proton VPN app's real log on a Windows 10 or 11 desktop, and qBittorrent 5.x. If you run either, an issue saying which versions worked is the most useful thing you can send.

Known issues

  • Proton doesn't document the Port pair line. If an update changes it, sync.log says so and nothing else breaks.
  • A console window can flash at sign-in: -WindowStyle Hidden hides it only after PowerShell has started.
  • qBittorrent has to be running to receive the port. A change made while it's closed lands within 60 s of opening it.