GluetunでTorrentアプリのポート転送を設定する方法
ダウンロードできるのに外部接続がない原因を解説します。Gluetunのポート転送を設定し、再接続のたびに新しいポートをTorrentクライアントへ反映して確認する手順です。
ポート転送なしでは外部から接続できない理由
Gluetun のポート転送は、VPN プロバイダーに対して、出口アドレス上の公開ポートをコンテナへ転送するよう要求します。これは、別のピアが torrent クライアントへの接続を開始するための唯一の方法です。その転送がなければ、トンネルは正常で、ダウンロードも実行されますが、外部から接続が開始されることはありません。確立する接続はすべて、クライアント側から先に開始したものです。
この仕組みは NAT(network address translation)です。コンテナは、他の多数の顧客とプロバイダーの出口アドレスを共有します。クライアントが外部への接続を開くと、プロバイダーはその通信フローを記録し、応答をトンネル経由で返します。外部のピアが内向きに接続しても、対応する記録済みのフローがないため、パケットは出口アドレスまで到達した時点で破棄されます。クライアント自身が接続を受け付けられるすべてのピアには接続できるため、ダウンロードは完了し、この問題は見えにくいままです。問題が現れるのはシード時です。シーダーとは、他のユーザーから接続されるマシンだからです。
受信ポートを開くと、2 つの点が変わります。自分からの接続を受け付けられないピアも接続できるため、スウォームにより早く参加できます。また、それらのピアへアップロードできるようになります。
VPN プロバイダーの多くがポート転送を提供しない理由
ポート転送は、共有アドレス上の限られたリソースです。プロバイダーは、1 つの出口 IP の 1 つのポート番号を 1 人の顧客に割り当て、そのポートで顧客が行う通信に対応します。大手プロバイダーのいくつかは、この機能を廃止し、理由として不正利用への対応を挙げています。サポートの有無は、単純なチェック項目ではなく、条件を確認する質問として扱ってください。現在ポート転送を提供しているか、自分のプランで利用できるか、実際に選択できるサーバーで利用できるかを確認します。
ポート転送を利用できる場合でも、ポートは動的です。ポートはアカウントではなく VPN セッションに割り当てられるため、再接続するたびに番号が変わることがあります。Private Internet Access は、gluetun が更新する署名付きポートを発行します。上流のドキュメントによると、/gluetun ディレクトリを bind mount して状態を再起動後も保持すれば、同じポートを 60 日間使用できます。ProtonVPN は、NAT-PMP (NAT port mapping protocol) を介してランダムなポートを短期間のリースで割り当てます。このリースは継続的に更新する必要があります。そのため、クライアントでポートを 1 回設定しただけでは、継続して機能しません。
gluetun がポートを要求できるプロバイダー
gluetun v3.41.3(2026 年 7 月 30 日リリース)時点で、組み込み連携が検証するプロバイダー名は Private Internet Access、ProtonVPN、Perfect Privacy、PrivateVPN の 4 つです。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 か所で公開し、いずれにも同じ値が入ります。
取得するたびに、ポートを 1 回ログに記録します。行は port forwarded is 45678 です。リクエストで値が返らなかった場合は no port forwarded になります。
docker logs gluetun 2>&1 | grep -i "port forwarded"VPN_PORT_FORWARDING_STATUS_FILE で指定されたファイルにポート番号を書き込みます。デフォルトは /tmp/gluetun/forwarded_port です。ファイルには 1 行につき 1 つのポートが入り、mode 0644 で書き込まれ、コンテナの PUID と PGID に chown されます。転送を停止すると、gluetun はファイルを削除せずに内容を消去します。そのため、利用側はファイルがない状態に遭遇せず、空のファイルを読み取れます。
docker exec gluetun cat /tmp/gluetun/forwarded_portコントロールサーバーでも値を提供します。コントロールサーバーはデフォルトで :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 が問い合わせできず、帯域外で静的ポートを割り当てられ、それを手動で許可する必要がある場合です。
3 つの方法のうち、永続的なのは 1 つだけで、残り 2 つは永続的ではありません。upstream のドキュメントでは、ステータスファイルは v4.0.0 で deprecated とされています。また、GET /v1/openvpn/portforwarded はすでに 301 Moved Permanently を返し、/v1/portforward を指しています。新しい処理では、コントロールサーバーを読み取ってください。
再接続のたびにクライアントへポートを通知する必要がある理由
Torrent クライアントは待ち受けポートを自身の設定に保存し、再起動後もその番号を使い続けます。転送されるポートは VPN セッションの属性です。再接続後はこの2つの番号が一致しなくなるため、プロバイダーは何も待ち受けていないポートへ転送し、クライアントは何も転送されていないポートで待ち受けます。再接続は珍しくありません。コンテナの再起動、サーバーの変更、切断されたトンネルを gluetun のヘルスチェックが再起動する場合、またはリースを更新できなかった場合に発生します。その結果、昨日までは到達可能だった構成が、どちらのログにもエラーを残さないまま、今日は到達不能になります。
そのため、gluetun がポートを取得した時点で、そのポートを適用する必要があります。接続方法は2つあり、どのプロセスが処理を担うかが異なります。
オプション 1: gluetun が up コマンドでポートを渡す
VPN_PORT_FORWARDING_UP_COMMAND はポートフォワーディングが有効になったときに実行され、VPN_PORT_FORWARDING_DOWN_COMMAND は無効になったときに実行されます。Gluetun はコマンドを実行する前に、{{PORT}}(最初のポート)、{{PORTS}}(すべてのポートをカンマ区切りにした値)、{{VPN_INTERFACE}}(トンネルインターフェース名。デフォルトは tun0)を置き換えます。シェル構文を使う場合は、明示的な /bin/sh -c ラッパーが必要です。以下は upstream の qBittorrent 例を、compose の環境変数エントリ 2 つとして記述したものです。
- 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 をトンネルにバインドします。random_port を false に設定すると、次回の起動時に qBittorrent が独自のポートを選択しなくなります。upnp を false に設定すると、存在しないルーター経由でポートをマッピングしようとしなくなります。
この方法には 2 つの要件があります。gluetun のネットワーク名前空間をクライアントが共有している場合は自動的に満たされますが、qBittorrent の Web UI が gluetun コンテナ内から 127.0.0.1:8080 で応答できなければなりません。また、コマンドは認証情報を送信しないため、Bypass authentication for clients on localhost(bypass_local_auth)を有効にする必要があります。切断後に qBittorrent が必ずしもポートを再確立しないため、down コマンドも指定します。
このコマンドは、Alpine をベースに構築され、wget を含む gluetun コンテナ内で実行されます。そのイメージには curl がありません。イメージに存在しないバイナリを指定したコマンドは、ポートフォワーディングが有効になるたびに必ず失敗します。
gluetun 外部のプロセスでポートを読み取る
もう 1 つの構成では、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 ルートが auth = "none" を持つ public というデフォルトロールに属しています。そのため、認証情報なしで応答し、gluetun は route GET /v1/portforward is unprotected by default, please set up authentication で始まる警告をログに記録します。upstream は後のリリースでこの経路を閉じる予定です。今のうちに、/gluetun/auth/config.toml に bind mount するファイルでロールを定義してください。
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 でエンコードした環境変数 1 つで同じように指定できます。ロールを指定せずにポート 8000 を公開すると、そのポートに到達できる全員が VPN の状態を制御できます。そのため、ホストや他のコンテナから gluetun に接続する方法を確認し、どこまで到達可能にするかを意図的に決めてください。
クライアントが 1 回の wget 呼び出しで操作できる API を公開している場合は、up コマンドを選択してください。イベントごとに 1 回だけ実行され、常駐する必要もありません。クライアントにログインフロー、設定ファイルの書き換え、または再起動が必要な場合は、外部プロセスを選択します。1 つの gluetun コンテナの背後に配置した arr スタックでは、通常は小さなポーラー 1 つにまとめます。ポートを必要とするのが torrent クライアントだけだからです。
落とし穴: namespace を共有しても待ち受けポートは設定されません
この問題では、原因の特定に最も時間がかかります。network_mode: "service:gluetun" はクライアントを gluetun の network namespace に配置するため、VPN アドレス、トンネルのルート、gluetun のファイアウォールルールを使用します。ただし、これらによってクライアントの待ち受けポートが設定されるわけではありません。gluetun は VPN インターフェース上で転送ポートを開き、そのポート宛てのパケットは namespace に到達します。クライアントが別のポートで待ち受けている場合、カーネルにはパケットの配送先がありません。すべての送信接続の確認が正常でも、接続は拒否されるかタイムアウトします。転送ポートとクライアントの待ち受けポートは別々の番号であり、両者を一致させることが必要です。
推測せず、両者を比較してください。以下の2つのコマンドは同じ 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]*'もう1つの設定も、誤った方向へ切り分けを進める原因になります。VPN_PORT_FORWARDING_LISTENING_PORT は、iptables を使用して転送ポート宛ての受信トラフィックを固定されたローカルポートへ転送します。Upstream は、torrent クライアントではこの設定を使用しないよう案内しています。クライアントは自身の待ち受けポートを trackers と peers に通知するため、swarm に誤ったポート番号が伝わるからです。
転送ポートに到達できることを確認する方法
クライアントに表示される接続インジケーターは、外向きの tracker 接続を反映しています。そのため、誰も接続できない状態でも緑色になることがあります。トンネル外のネットワークから、自分で管理する listener を使ってテストします。Upstream はこの用途の小さなツールを公開しています。2 つのプロセスは同じポートに bind できないため、最初に torrent クライアントを停止してください。
docker stop qbittorrent
docker exec -it gluetun /bin/shコンテナ内で、amd64 を CPU アーキテクチャに合わせ、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 が使用している exit address を確認します。応答は JSON 形式で、アドレスは public_ip フィールドに含まれています。
curl -s http://127.0.0.1:8000/v1/publicip/ip同じ VPN に接続していないデバイスから http://<that address>:4567 を開きます。モバイルデータ通信中のスマートフォンを使用できます。ブラウザーの IP アドレスと user agent を表示するページが開き、port-checker に一致するリクエストが記録されていれば、namespace まで inbound TCP が到達しています。timeout になる場合は到達しておらず、原因はクライアントより上位にあります。CTRL+C でツールを停止し、exit で shell を終了してから、クライアントを再度起動します。この確認で検証できるのは TCP だけです。DHT (distributed hash table) と uTP のトラフィックは同じポート番号で UDP を使用しますが、このテストでは確認できません。
失敗するケースと表示される文字列
ログにポート行がまったくない。 ポートを要求する処理が実行されていません。docker exec gluetun printenv | grep PORT_FORWARDING で変数が実際にコンテナへ渡っていることを確認してください。誤った compose service に変数を設定していることがよくある原因です。
Gluetun が起動せず、provider についてエラーを出す。 VPN_PORT_FORWARDING_PROVIDER はサポート対象の4つの名前と照合されます。入力ミスがあると、ポート転送なしで静かに実行されるのではなく、コンテナが停止します。
ログに no port forwarded と表示される。 Gluetun は要求しましたが、provider から応答がありませんでした。ProtonVPN では通常、生成した設定で NAT-PMP が有効になっていないか、契約プランにポート転送が含まれていないことを意味します。Private Internet Access では通常、選択したサーバーがポート転送に対応していないことを意味します。
ポートは割り当てられるが、接続を受け付けない。 上記の2つのコマンドで、転送ポートとクライアントの待ち受けポートを比較してください。一致している場合は、クライアントがトンネルインターフェースに bind されていることと、random-port オプションが無効であることを確認します。このオプションを有効にすると、起動のたびに待ち受けポートが書き換えられます。
up コマンドを実行しても何も起きないように見える。 コンテナ内で正確なコマンドを実行し、エラーを確認してください。docker exec gluetun /bin/sh -c '<your command>'。通常は curl: not found になります。イメージに含まれているのは wget だけだからです。
control server から 401 Unauthorized が返る。 auth config は定義されていますが、role に呼び出している route が記載されていません。route は method と path の組み合わせで照合されるため、role に /v1/portforward だけを記載しても GET /v1/portforward は許可されません。
Private Internet Access で、再起動のたびに別のポートが割り当てられる。 /gluetun を bind mount し、保存されたポート状態が再起動後も残るようにしてください。この volume がない場合、gluetun は毎回新しいポートを要求します。
FAQ
トレントはダウンロードできますが、着信接続を受けられないのはなぜですか?
ポート転送がない場合、VPN プロバイダーには、任意のポートへの着信パケットをトンネルへ転送する NAT ルールがありません。そのため、自分から開始していない接続は出口アドレスで破棄されます。クライアント自身が接続を開始するため、ダウンロードは引き続き機能します。接続可能なピアには接続できます。一方、シードやスウォームへの参加は、他のユーザーから接続される必要があるため影響を受けます。対処するには、ポート転送を提供するプロバイダーを使用し、VPN_PORT_FORWARDING=on を gluetun で設定して、割り当てられたポートをクライアントの待ち受けポートに適用します。
gluetun は、ポート転送を提供するすべての VPN プロバイダーで動作しますか?
いいえ。gluetun v3.41.3 がネイティブで統合しているプロバイダーは、Private Internet Access、ProtonVPN、Perfect Privacy、PrivateVPN の4社です。この一覧にないプロバイダーは VPN_PORT_FORWARDING_PROVIDER の検証に失敗し、コンテナは起動時に停止します。プロバイダーが独自の管理画面で固定ポートを割り当てる場合、gluetun がそのポートを代わりに要求することはできません。ただし、FIREWALL_VPN_INPUT_PORTS により、その固定ポートを gluetun のファイアウォールで許可できます。プロバイダーのポリシーは変更されるため、この用途でプランを購入する前に、現在のプロバイダーのページを確認してください。
再接続するたびにポートを更新する必要がありますか?
はい。更新は自動化してください。転送ポートは VPN セッションに属します。そのため、コンテナの再起動、サーバーの変更、リース更新の失敗によって新しい番号が割り当てられることがあります。一方、クライアントは自身の設定に保存したポートを使い続けます。VPN_PORT_FORWARDING_UP_COMMAND を使って gluetun にポートを反映させるか、制御サーバーから GET /v1/portforward を読み取り、API 経由でクライアントに値を書き込む小さなプロセスを実行してください。VPN_PORT_FORWARDING_UP_COMMAND はポート転送が有効になった直後に実行されます。
転送ポートが実際に開いているか確認するにはどうすればよいですか?
gluetun のネットワーク名前空間内で、その正確なポートを待ち受けるプロセスを起動し、VPN の外部から接続します。まずトレントクライアントを停止してポートを解放します。次に、upstream の port-checker バイナリを gluetun コンテナ内で --listening-address=":<port>" とともに実行します。curl -s http://127.0.0.1:8000/v1/publicip/ip から出口アドレスを取得し、モバイルデータ通信中のスマートフォンから http://<address>:<port> を開きます。port-checker のログにリクエストが表示されれば、着信 TCP が到達しています。タイムアウトする場合は、クライアント自身のステータスアイコンに関係なく、着信していません。