Gluetun 連接埠轉送與 torrent client 設定
下載正常卻沒有任何連線進來?了解 gluetun 連接埠轉送、每次重新連線後同步新連接埠到 torrent client,並完成實際驗證。
沒有轉送連接埠時,為什麼沒有任何連線進來
Gluetun 的連接埠轉送功能會要求 VPN 供應商,將其出口位址上的一個公開連接埠對應回你的容器。這是其他 peer 能向 torrent client 建立連線的唯一方式。沒有這項對應時,tunnel 仍然正常、下載也會執行,但不會有連線自行進入。所有正常建立的連線,都是由你的 client 先發起。
這套機制是 NAT(network address translation,網路位址轉譯)。你的容器會與許多其他客戶共用供應商的出口位址。當 client 向外建立連線時,供應商會記錄該連線流程,並將回應透過 tunnel 傳回。陌生 peer 從外部連入時,找不到相符的既有連線流程,因此封包抵達出口位址後就會在那裡遭到丟棄。你的 client 仍能連線到本身可接受連線的每個 peer,因此下載仍可完成,問題也不容易被發現。問題會在做種時顯現,因為 seeder 是其他人會連線到的機器。
開放一個入站連接埠會帶來兩項變化。你能更快加入 swarm,因為無法自行接受連線的 peer 現在可以連到你;同時,你也能向這些 peer 上傳資料。
為什麼大多數 VPN 供應商不提供連接埠轉送
連接埠轉送是共用位址上的稀缺資源。供應商會在一個出口 IP 上保留一個連接埠號碼給一名客戶,並負責處理該客戶透過此連接埠進行的所有活動。多家大型供應商已移除這項功能,理由是需要處理濫用問題。評估支援情況時,應將其視為分類問題,而不是單純的核取方塊:確認供應商目前是否提供連接埠轉送、你的方案是否包含此功能,以及你是否能實際選擇支援此功能的伺服器。
提供連接埠轉送時,該連接埠通常是動態的。它隸屬於 VPN 工作階段,而不是你的帳戶,因此每次重新連線後都可能取得不同的號碼。Private Internet Access 會發放簽署的連接埠,由 gluetun 進行更新;上游文件指出,只要將 /gluetun 目錄 bind mount,使狀態可在重新啟動後保留,就能在 60 days 內維持相同的連接埠。ProtonVPN 會透過 NAT-PMP (NAT port mapping protocol) 指派隨機連接埠,但租約時間較短,必須持續續租。因此,只在 client 中設定一次連接埠,無法持續正常運作。
gluetun 可向哪些供應商要求連接埠
截至 2026 年 7 月 30 日發布的 gluetun v3.41.3,原生整合會驗證 4 個供應商名稱:Private Internet Access、ProtonVPN、Perfect Privacy 和 PrivateVPN。使用 VPN_PORT_FORWARDING=on 啟用此功能,預設值為 off。較舊的指南會使用 PORT_FORWARDING 或 PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING。這兩個名稱在此版本中仍可作為相容舊版的名稱使用,但即將淘汰。
有 2 項供應商設定會決定要求是否能夠成功。ProtonVPN 需要付費方案,且必須啟用 NAT-PMP:產生 WireGuard 設定時,請在 VPN 選項中啟用 NAT-PMP (Port Forwarding);使用 OpenVPN 時,請將 +pmp 附加到使用者名稱。Private Internet Access 的 OpenVPN 設定包含 PORT_FORWARD_ONLY,會將伺服器選擇限制在支援連接埠轉送的伺服器,避免連線到從未支援此功能的伺服器。WireGuard 與 OpenVPN 要求連接埠的方式不同,因此請先閱讀供應商的說明頁面,再選擇使用方式。
gluetun 使用自訂設定而非內建供應商時,VPN_PORT_FORWARDING_PROVIDER 會指定 gluetun 應呼叫的 API。上游 Private Internet Access 頁面會將此變數與 VPN_PORT_FORWARDING_USERNAME 和 VPN_PORT_FORWARDING_PASSWORD 搭配使用;這些變數會提供連接埠要求所需的帳戶認證資訊。
在 docker compose 中啟用 gluetun 連接埠轉送
這裡假設通道已正常運作。若尚未運作,請先依照將 Docker 容器流量透過 gluetun 路由操作,確認下載可以執行後再回到這裡。
services:
gluetun:
image: qmcgaw/gluetun:v3.41.3
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- 8080:8080/tcp
- 8000:8000/tcp
volumes:
- ./gluetun:/gluetun
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
- VPN_PORT_FORWARDING=on
- TZ=Etc/UTC
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:5.2.3
container_name: qbittorrent
network_mode: "service:gluetun"
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- WEBUI_PORT=8080
volumes:
- ./qbittorrent:/config
- ./downloads:/downloads
depends_on:
- gluetun
restart: unless-stopped固定映像標籤。qmcgaw/gluetun:latest 會跟隨 master 分支,而連接埠轉送的內部實作正在為 v4 變更,因此未固定的映像可能在下次 docker compose pull 時改變行為。請使用用於 compose secret 的 env 檔案,不要將私密金鑰放在 compose 檔案中。
Gluetun 寫入轉送連接埠的位置
Gluetun 會在 3 個位置公開連接埠,而且這些位置的值都相同。
每次取得連接埠時,Gluetun 都會記錄一次。該行內容為 port forwarded is 45678;如果要求未取得任何值,則為 no port forwarded。
docker logs gluetun 2>&1 | grep -i "port forwarded"Gluetun 會將數字寫入由 VPN_PORT_FORWARDING_STATUS_FILE 指定的檔案,預設為 /tmp/gluetun/forwarded_port。檔案每行包含 1 個連接埠,檔案模式為 0644,並會將擁有者與群組變更為容器的 PUID 和 PGID。停止轉送時,Gluetun 會清空檔案而不刪除檔案,因此讀取端會讀到空檔案,不會因檔案不存在而失敗。
docker exec gluetun cat /tmp/gluetun/forwarded_portGluetun 也會在 control server 上提供此值。該伺服器預設監聽 :8000,並由 HTTP_CONTROL_SERVER_ADDRESS 設定。
curl -s http://127.0.0.1:8000/v1/portforward{"port":45678,"ports":[45678]}Gluetun 也會在 VPN 介面上的自有防火牆中開放該連接埠,因此使用原生整合功能時不需要 FIREWALL_VPN_INPUT_PORTS。此變數適用於另一種情況:Gluetun 無法查詢該 provider,而你是透過其他管道取得固定連接埠,必須手動放行該連接埠。
這 3 種方式中,只有 1 種具持久性,另外 2 種則沒有。上游文件已將 status file 標記為在 v4.0.0 中棄用,而 GET /v1/openvpn/portforwarded 已經會回覆 301 Moved Permanently,並指向 /v1/portforward。新的實作應讀取 control server。
每次重新連線時都必須告知用戶端連接埠
torrent 用戶端會將監聽連接埠儲存在自己的設定中,並在重新啟動後沿用該連接埠。轉送的連接埠則屬於 VPN 工作階段。重新連線後,這兩個連接埠會不一致,因此供應商會將連接埠對應到沒有服務監聽的連接埠,而用戶端則監聽沒有對應轉送的連接埠。重新連線並不罕見:可能是容器重新啟動、伺服器變更、連線中斷後由 gluetun 的 health check 重新啟動,或租約無法續期。結果是原本昨天仍可連線的設定,今天會在沒有任何錯誤日誌的情況下悄悄變得無法連線。
因此,必須在 gluetun 取得連接埠的當下套用該連接埠。有兩種方式可以完成整合,差別在於由哪個程序執行這項工作。
選項 1:gluetun 使用 up command 推送連接埠
VPN_PORT_FORWARDING_UP_COMMAND 會在連接埠轉送啟用時執行,VPN_PORT_FORWARDING_DOWN_COMMAND 會在連接埠轉送停用時執行。Gluetun 會在執行 command 前替換 {{PORT}}(第一個連接埠)、{{PORTS}}(所有連接埠,以逗號分隔)及 {{VPN_INTERFACE}}(tunnel 介面名稱,預設為 tun0)。Shell 語法需要明確的 /bin/sh -c wrapper。以下是 upstream 的 qBittorrent 範例,以兩個 compose environment entries 表示:
- VPN_PORT_FORWARDING_UP_COMMAND=/bin/sh -c 'wget -O- -nv --retry-connrefused --post-data "json={\"listen_port\":{{PORT}},\"current_network_interface\":\"{{VPN_INTERFACE}}\",\"random_port\":false,\"upnp\":false}" http://127.0.0.1:8080/api/v2/app/setPreferences'
- VPN_PORT_FORWARDING_DOWN_COMMAND=/bin/sh -c 'wget -O- -nv --retry-connrefused --post-data "json={\"listen_port\":0,\"current_network_interface\":\"lo\"}" http://127.0.0.1:8080/api/v2/app/setPreferences'該呼叫中的每個欄位都有特定用途。listen_port 是新的連接埠。current_network_interface 會將 qBittorrent 綁定至 tunnel。將 random_port 設為 false,可防止 qBittorrent 在下次啟動時自行選擇連接埠。將 upnp 設為 false,可防止它嘗試透過不存在的 router 對映連接埠。
這種作法有兩項要求。qBittorrent 的 web UI 必須能從 gluetun container 內連線至 127.0.0.1:8080;當 client 共用 gluetun 的 network namespace 時,這會自動成立。此外,必須啟用 Bypass authentication for clients on localhost(bypass_local_auth),因為 command 不會傳送認證資訊。加入 down command 是因為 qBittorrent 在中斷連線後不一定會重新建立連接埠轉送。
此 command 會在 gluetun container 內執行。該 container 以 Alpine 為基礎,並附帶 wget。該 image 中沒有 curl。如果 command 指定的 binary 不存在於該 image 中,每次連接埠轉送啟用時都會失敗。
選項 2:gluetun 外部的程序讀取連接埠
另一種模式是在 gluetun 旁執行小型程序。該程序取得連接埠,並透過用戶端自身的 API 將連接埠寫入用戶端。從控制伺服器讀取:
port=$(curl -s http://127.0.0.1:8000/v1/portforward | jq -r .port)如果該程序能看見檔案,也可以直接讀取檔案。/tmp/gluetun/forwarded_port 位於 gluetun 容器內,因此 sidecar 必須在兩個容器中都將共用磁碟區掛載至 /tmp/gluetun;或者,將 VPN_PORT_FORWARDING_STATUS_FILE 指向現有掛載磁碟區下的路徑。
此處必須處理驗證。在 v3.41.3 中,路由 GET /v1/portforward 屬於名為 public 的預設角色,該角色具有 auth = "none",因此不需要憑證即可回應;gluetun 也會記錄以 route GET /v1/portforward is unprotected by default, please set up authentication 開頭的警告。上游將在後續版本關閉這個缺口。請現在就在 bind mount 至 /gluetun/auth/config.toml 的檔案中定義角色:
roles = [
{ name = "qbittorrent", routes = ["GET /v1/portforward"], auth = "apikey", apikey = "myapikey" }
]使用 docker run --rm qmcgaw/gluetun:v3.41.3 genkey 產生金鑰,並將金鑰放入 X-API-Key 標頭。若不想掛載檔案,HTTP_CONTROL_SERVER_AUTH_DEFAULT_ROLE 可透過一個以 JSON 編碼的環境變數完成相同工作。未設定角色便公開 Port 8000,任何能連線到該連接埠的對象都可控制 VPN 狀態。因此,規劃 如何讓主機與其他容器連線至 gluetun 時,應明確決定它的可達範圍。
當用戶端提供可由一次 wget 呼叫驅動的 API 時,請選擇 up command。它每個事件只執行一次,不會增加需要持續執行的程序。當用戶端需要登入流程、重寫設定檔或重新啟動時,請選擇外部程序。在 單一 gluetun 容器後方的 an stack 中,通常只需要一個小型輪詢程序,因為只有 torrent 用戶端需要這個連接埠。
陷阱:共用 network namespace 不會設定監聽連接埠
這個問題最浪費排查時間。network_mode: "service:gluetun" 會將用戶端放入 gluetun 的 network namespace,因此用戶端會取得 VPN 位址、通道路由和 gluetun 的防火牆規則。但這些設定都不會設定用戶端的監聽連接埠。Gluetun 會在 VPN 介面上開啟轉送連接埠,前往該連接埠的封包會抵達這個 namespace;如果用戶端監聽的是另一個連接埠,kernel 就沒有對應的目標可以交付封包。此時連線會遭拒或逾時,但所有對外連線檢查看起來都正常。轉送連接埠和用戶端的監聽連接埠是兩個獨立的數字,讓兩者保持一致就是完整的設定工作。
不要猜測,直接比較兩者。以下兩個指令都會針對同一個 namespace 執行:
docker exec gluetun cat /tmp/gluetun/forwarded_port
docker exec gluetun wget -qO- http://127.0.0.1:8080/api/v2/app/preferences | grep -o '"listen_port":[0-9]*'另一項設定也會讓人朝錯誤方向排查。VPN_PORT_FORWARDING_LISTENING_PORT 會使用 iptables,將來自轉送連接埠的入站流量重新導向至固定的本機連接埠。上游文件建議不要將它與 torrent 用戶端搭配使用,因為用戶端會向 tracker 和對等節點宣告自己的監聽連接埠,導致 swarm 得知錯誤的連接埠號碼。
如何證明轉送的連接埠可連線
用戶端自身的連線指示器反映的是對外的 tracker 連線,因此即使沒有任何連線能進入你的服務,指示器仍可能顯示綠色。請使用你能控制的 listener,並從 VPN tunnel 外部的網路進行測試。上游提供了一個小型工具。請先停止 torrent client,因為兩個程序無法繫結同一個連接埠。
docker stop qbittorrent
docker exec -it gluetun /bin/sh在 container 內,將 amd64 改為符合 CPU architecture 的值,並將 4567 改為轉送的連接埠:
wget -qO port-checker https://github.com/qdm12/port-checker/releases/download/v0.4.0/port-checker_0.4.0_linux_amd64
chmod +x port-checker
./port-checker --listening-address=":4567"現在找出 gluetun 使用的出口位址。回應格式為 JSON,位址位於 public_ip 欄位。
curl -s http://127.0.0.1:8000/v1/publicip/ip從不在同一個 VPN 上的裝置開啟 http://<that address>:4567。使用行動數據的手機即可。若頁面顯示瀏覽器的 IP 位址與 user agent,且 port-checker 的日誌中也記錄到相符的請求,表示 TCP 封包已抵達該 namespace。若發生逾時,表示封包未抵達,原因位於用戶端之外。使用 CTRL+C 停止工具,使用 exit 離開 shell,然後重新啟動用戶端。此檢查僅測試 TCP。DHT(distributed hash table)與 uTP 流量會在相同的連接埠號碼上使用 UDP,因此不在此測試範圍內。
失敗情況與你會看到的字串
日誌中完全沒有連接埠行。 沒有任何元件要求連接埠。使用 docker exec gluetun printenv | grep PORT_FORWARDING 確認變數確實已傳入容器,因為將變數設在錯誤的 compose 服務中是常見原因。
Gluetun 拒絕啟動,並顯示 provider 相關錯誤。 VPN_PORT_FORWARDING_PROVIDER 會與支援的 4 個名稱比對,因此拼字錯誤會使容器停止,而不是在未轉送連接埠的情況下靜默執行。
日誌顯示 no port forwarded。 Gluetun 發出請求,但 provider 沒有回應。在 ProtonVPN 上,通常表示產生的設定未啟用 NAT-PMP,或方案不包含連接埠轉送。在 Private Internet Access 上,通常表示選取的伺服器不提供此功能。
已取得連接埠,但沒有連入連線。 使用上方的 2 個命令,比對轉送連接埠與用戶端的監聽連接埠。如果兩者相同,請確認用戶端已繫結至 tunnel 介面,並關閉 random-port 選項,因為此選項會在每次啟動時改寫監聽連接埠。
up 命令似乎沒有作用。 在容器內執行確切的命令以查看錯誤:docker exec gluetun /bin/sh -c '<your command>'。通常會得到 curl: not found,因為此映像檔只提供 wget。
控制伺服器顯示 401 Unauthorized。 你已定義 auth 設定,但 role 未列出你呼叫的 route。Route 會以 method 加 path 進行比對,因此只列出 /v1/portforward 的 role 不涵蓋 GET /v1/portforward。
Private Internet Access 每次重新啟動後都使用不同的連接埠。 Bind mount /gluetun,讓已儲存的連接埠狀態在重新啟動後仍可保留。沒有此 volume 時,gluetun 每次都會要求新的連接埠。
FAQ
為什麼我的 torrents 可以下載,卻始終沒有傳入連線?
如果沒有轉送連接埠,VPN provider 就沒有 NAT 規則可將任何連接埠上的傳入封包轉送至你的 tunnel。因此,從外部發起、而不是由你建立的連線,會在出口位址被丟棄。下載仍可正常運作,因為 client 會自行建立這些連線,也能連線至任何可接受連線的 peer。Seed 與加入 swarm 會受到影響,因為兩者都需要其他使用者連線到你。解決方式是使用提供 port forwarding 的 provider、在 gluetun 中設定 VPN_PORT_FORWARDING=on,並將產生的連接埠套用至 client 的 listening port。
gluetun 是否支援任何 VPN provider 的 port forwarding?
不支援。Gluetun v3.41.3 原生整合 4 個 provider:Private Internet Access、ProtonVPN、Perfect Privacy 和 PrivateVPN。清單以外的 provider 會在 VPN_PORT_FORWARDING_PROVIDER 驗證失敗,容器也會在啟動時停止。如果你的 provider 透過自己的控制面板提供 static port,gluetun 無法代你要求該連接埠,但 FIREWALL_VPN_INPUT_PORTS 可讓該固定連接埠通過 gluetun 的防火牆。Provider 政策可能變更,因此在購買方案前,請先查看目前的 provider 頁面。
每次重新連線後,都必須更新連接埠嗎?
需要,而且應自動更新。轉送的連接埠屬於 VPN session,因此容器重新啟動、server 變更或租約更新失敗,都可能產生新的號碼;但 client 仍會使用儲存在自身設定中的連接埠。你可以讓 gluetun 透過 VPN_PORT_FORWARDING_UP_COMMAND 推送連接埠;此操作會在 port forwarding 啟用時立即執行。也可以執行小型程序,從 control server 讀取 GET /v1/portforward,再透過 client 的 API 將數值寫入 client。
如何確認轉送的連接埠確實開放?
在 gluetun 的 network namespace 內,於該確切連接埠上執行 listener,並從 VPN 外部連線至該 listener。先停止 torrent client,確保連接埠可用,再使用 --listening-address=":<port>",在 gluetun container 內執行 upstream port-checker binary。透過 curl -s http://127.0.0.1:8000/v1/publicip/ip 取得出口位址,然後使用行動數據的手機開啟 http://<address>:<port>。如果 port-checker log 中出現請求,即可證明傳入 TCP 已抵達。如果發生逾時,表示封包沒有抵達,不論 client 自身的狀態圖示顯示什麼。