SSD Nodes Learn 🎉 VPS mula $5.50/buwan
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-13

Gluetun: Paano Maabot ang Host at Ibang Container

Walang sariling interfaces ang container sa likod ng Gluetun. I-publish ang ports sa Gluetun at payagan lang ang subnet na kailangan nitong maabot sa labas ng tunnel.

Ano ang nangyayari kapag sumali ang isang container sa network ng gluetun

Ang container na nagse-set ng network_mode: service:gluetun ay walang sariling network interfaces. Sumasali ito sa network namespace ng gluetun, kaya ang port publishing at firewall rules ay hindi na katangian ng container na iyon. Sa halip, nagiging katangian na ang mga ito ng gluetun service. Lahat ng sagot sa ibaba ay nagmumula sa iisang fact na ito.

Ang network namespace ay pribadong kopya ng kernel ng isang network stack. May sarili itong interfaces, routing table, firewall rules, at listening sockets. Bilang default, nagbibigay ang Docker ng tig-iisang namespace sa bawat container. Kapag isinulat mo ang network_mode: service:gluetun, nilalaktawan ng Docker ang hakbang na iyon at inilalagay ang bagong container sa namespace na pagmamay-ari na ng gluetun. Nananatili sa container ang sarili nitong filesystem at ang sarili nitong /etc/hosts file. Magiging mahalaga ang ikalawang ito sa susunod.

Makikita mo ito nang direkta.

docker inspect -f '{{.HostConfig.NetworkMode}}' qbittorrent

Ipi-print nito ang container: na sinusundan ng container ID ng gluetun, samantalang bridge ang ipi-print ng isang normal na container. Dito nagpapatuloy ang guide mula sa pag-route ng Docker traffic sa VPN gamit ang gluetun: gumagana ang tunnel, at ngayon ay walang makakakonekta sa container.

I-publish ang port sa gluetun, hindi sa application

Mag-iwan ng ports: block sa service na nagse-set ng network_mode, at tatanggi ang Docker na gumawa ng container:

Error response from daemon: conflicting options: port publishing and the container type network mode

Direkta ang dahilan. Ang pag-publish ng port ay pagdaragdag ng NAT (network address translation) rule na nagfo-forward ng host port papunta sa sariling network namespace ng container. Walang sariling network namespace ang container na ito. Ilipat ang mapping sa gluetun service. Hindi nagbabago ang port number dahil nakikinig pa rin ang application sa port na iyon sa loob ng shared namespace.

services:
  gluetun:
    ports:
      - "8080:8080/tcp"   # qBittorrent web UI

  qbittorrent:
    network_mode: "service:gluetun"
    # no ports: block here

Wala ring silbi ang expose: block sa dependent service. Ang networks: block naman doon ay agarang nagdudulot ng failure: iniuulat ng Compose na nagdeklara ang service ng mutually exclusive na network_mode at networks, kaya tumatanggi itong i-load ang file.

May isang epekto na lalabas sa kalaunan. Iisang port space ang ginagamit ng lahat ng container sa namespace. Kaya nagkakaroon ng conflict kapag parehong default na 8080 ang dalawang application. Magfa-fail ang pangalawang magsisimula at maglalabas ng error na ginagamit na ang address. Baguhin ang isa sa mga ito sa sarili nitong configuration. Halimbawa, baguhin ang WEBUI_PORT variable sa LinuxServer qBittorrent image, pagkatapos ay i-publish ang bagong number sa gluetun.

Paano nagkakakonekta ang mga container sa likod ng gluetun?

Sa loob ng namespace, mayroon na silang shared loopback interface. Nakakakonekta ang container na nasa likod ng gluetun sa sibling nito sa 127.0.0.1:<port> nang walang Docker network.

Mula sa labas ng namespace, walang sariling pangalan ang container. Nireso-resolve ng embedded DNS ng Docker ang service name sa address ng service sa isang user-defined network, ngunit walang address ang container na ito sa anumang network. Kaya hindi direktang nakakakonekta ang isang normal na container gaya ng Sonarr sa torrent client sa http://qbittorrent:8080. Sa halip, kumokonekta ito sa http://gluetun:8080, dahil nakikinig ang socket sa namespace ng gluetun at sa address ng gluetun. Nakalilito ito para sa mga pamilyar sa kung paano gumagana ang Docker Compose networks at service names at umaasang gagana ang karaniwang naming. Gumagana rin ito nang walang anumang port na naka-publish sa host, dahil nasa iisang Compose network ang dalawang container.

Suriin muna ang DNS bago mag-debug ng iba. Sariling resolver ang pinapatakbo ng Gluetun at nire-rewrite nito ang /etc/resolv.conf sa sarili nitong container, ngunit per-container na file ang /etc/resolv.conf, kaya hindi ang isinulat ng gluetun ang file na binabasa ng application mo.

docker exec qbittorrent cat /etc/resolv.conf

Paano maabot ang service na tumatakbo sa Docker host?

Gamitin ang host.docker.internal. Kailangan nito ng dalawang setting sa dalawang magkaibang lugar dahil dalawang magkaibang bahagi ang may problema.

Unahin ang pangalan. Per container ang /etc/hosts, kaya sa application container dapat ilagay ang entry na extra_hosts, hindi sa gluetun.

  prowlarr:
    network_mode: "service:gluetun"
    extra_hosts:
      - "host.docker.internal:host-gateway"

Espesyal na value ang host-gateway na pinapalitan ng Docker ng internal address ng host mismo. Sa plain Linux Docker install, ito ang address ng docker0 bridge, na karaniwang 172.17.0.1. Kumpirmahin ang iyo gamit ang ip -4 addr show docker0 sa VPS. Awtomatikong nire-resolve ng Docker Desktop ang pangalang ito. Kaya nilalaktawan ng mga guide na isinulat para sa laptop ang linyang extra_hosts, at nagfa-fail ang parehong file sa server.

Ikalawa ang route. Sinasabi lamang ng pagdaragdag ng pangalan kung aling address ang gagamitin ng container. Dumadaan pa rin ang packet sa default route ng gluetun, na siyang tunnel, at dini-drop ito ng firewall ng gluetun. Ang sintomas ay connection na nagha-hang at kalaunan ay nag-ti-time out, hindi connection na nire-refuse. Ang refusal ay nangangahulugang nakarating ang packet at may tumugong no. Ang timeout ay nangangahulugang hindi ito nakarating.

  gluetun:
    environment:
      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32

Pagkatapos, tiyaking aktuwal na nakikinig ang host service sa address na iyon. Ang PostgreSQL server na naka-bind lamang sa 127.0.0.1 ay hindi maaabot mula sa anumang container, may tunnel man o wala, dahil ang 127.0.0.1 sa loob ng namespace ay loopback ng mismong namespace. I-bind ito sa 172.17.0.1 sa halip. Tatanggap ito ng connections mula sa mga container habang hindi ito nakabukas sa public interface. I-verify gamit ang ss -lntp | grep 5432 sa host.

Ano talaga ang binabago ng FIREWALL_OUTBOUND_SUBNETS

Inilalarawan ito ng gluetun documentation bilang mga subnet na pinaghihiwalay ng kuwit at pinapayagang ma-access ng gluetun at ng mga container na nakikibahagi sa network stack nito. Binabanggit din dito na may kasama itong mga pagbabago sa firewall at routing. Mahalaga ang dalawang bahaging ito. Nagdaragdag ang gluetun ng route para sa bawat nakalistang subnet sa pamamagitan ng Docker bridge gateway, kaya dumadaan ang mga packet para sa mga address na iyon sa eth0 sa halip na sa tunnel. Binubuksan din nito ang firewall para sa mga address na iyon, dahil kung hindi, ibinabagsak ng gluetun ang outbound traffic na hindi nakalaan para sa VPN server.

Isulat ang value nang walang spaces pagkatapos ng mga kuwit.

      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,192.168.1.0/24,100.64.7.9/32

Madaling hindi mapansin ang dalawang property. Namespace-level setting ito, kaya nalalapat ito sa lahat ng container na nasa likod ng gluetun, hindi lamang sa container na nasa isip mo. Outbound lamang ito: kinokontrol nito ang mga connection na sinisimulan ng isang container. Iba ang path ng mga connection na dumarating sa isang published port, kaya hindi kailangang ilista ang mga ito rito.

Pag-access sa web UI mula sa isang Tailscale peer

Binibigyan ng Tailscale ang bawat machine ng address sa 100.64.0.0/10, ang range na nakalaan para sa carrier grade NAT. Magkaiba ang kailangang gawin para sa dalawang direksiyon.

Simple ang inbound. Kapag na-publish ang 8080:8080 sa gluetun, naka-bind ang port na iyon sa lahat ng address ng host. Kasama rito ang tailscale0 interface ng host, kaya maaaring buksan ng isang peer ang http://<machine-name>:8080 upang maabot ang container. Walang papel ang Gluetun sa path na ito dahil nasa host ang Docker NAT rule at nasa labas ito ng namespace.

Para maging reachable lamang ang UI sa tailnet, i-bind ang published port sa Tailscale address ng host sa halip na sa lahat ng address.

    ports:
      - "100.101.102.103:8080:8080/tcp"

Hanapin ang address na iyon gamit ang tailscale ip -4 sa host. Mas mahigpit na control dito ang binding kaysa firewall rule dahil hindi talaga binubuksan ang port sa public interface. Iniiwasan din nito ang problemang inilalarawan sa Direktang pag-publish ng Docker ng mga port lampas sa ufw.

Sa outbound lumalabas ang FIREWALL_OUTBOUND_SUBNETS. Kung kailangang tumawag ng container sa isang peer, idagdag ang address ng peer na iyon. Mas mainam ang /32 para sa bawat peer kaysa sa buong /10. Hindi magre-resolve sa loob ng container ang mga pangalan ng MagicDNS dahil hindi ginagamit ng container ang resolver ng host. Gamitin ang numeric na 100.x address, o i-pin ito gamit ang isang extra_hosts line. Pareho rin ito kapag nagpapatakbo ka ng sarili mong Tailscale control server gamit ang Headscale.

Isang kumpletong compose file para sa karaniwang setup

Isang download client na nasa likod ng VPN, dalawang web UI na sumasagot lamang sa tailnet, at isang container na nagbabasa mula sa PostgreSQL database na tumatakbo sa host.

services:
  gluetun:
    image: qmcgaw/gluetun:latest
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    ports:
      - "127.0.0.1:8000:8000/tcp"        # gluetun control server, host only
      - "100.101.102.103:8080:8080/tcp"  # qBittorrent UI, tailnet only
      - "100.101.102.103:9696:9696/tcp"  # Prowlarr UI, tailnet only
    volumes:
      - ./gluetun:/gluetun
    environment:
      - VPN_SERVICE_PROVIDER=mullvad
      - VPN_TYPE=wireguard
      - WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
      - WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
      - SERVER_CITIES=Amsterdam
      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,100.64.7.9/32
      - TZ=Europe/Amsterdam
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    network_mode: "service:gluetun"
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
      - WEBUI_PORT=8080
    volumes:
      - ./qbittorrent:/config
      - /srv/downloads:/downloads
    depends_on:
      gluetun:
        condition: service_healthy
    restart: unless-stopped

  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    network_mode: "service:gluetun"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
      - PROWLARR__POSTGRES__HOST=host.docker.internal
      - PROWLARR__POSTGRES__PORT=5432
      - PROWLARR__POSTGRES__USER=prowlarr
      - PROWLARR__POSTGRES__PASSWORD=${PROWLARR_DB_PASSWORD}
      - PROWLARR__POSTGRES__MAINDB=prowlarr-main
      - PROWLARR__POSTGRES__LOGDB=prowlarr-log
    volumes:
      - ./prowlarr:/config
    depends_on:
      gluetun:
        condition: service_healthy
    restart: unless-stopped

Basahin ang file para makita ang pattern, hindi ang mga pangalan ng produkto. Parehong naka-publish ang mga UI sa gluetun at naka-bind sa tailnet address ng host, kaya sumasagot ang mga ito sa Tailscale at wala nang iba. Tanging ang Prowlarr ang may linyang extra_hosts, dahil ito ang container na nagre-resolve ng host.docker.internal. Tinutukoy ng FIREWALL_OUTBOUND_SUBNETS ang dalawang single address: ang Docker bridge address ng host, para makapagbukas ang Prowlarr ng database connection, at isang tailnet peer.

Sadyang wala sa file ang PostgreSQL server. Tumatakbo ito sa VPS bilang ordinary system service na nakikinig sa 172.17.0.1:5432. Ito ang parehong layering gaya ng arr stack sa Docker Compose, ngunit inilipat ang database sa labas ng Docker.

Ilabas ang WireGuard private key sa compose file. Nagbabasa ang ${WIREGUARD_PRIVATE_KEY} mula sa isang .env file na nasa tabi nito. Ito ang pattern na tinatalakay sa env files at secrets para sa Docker Compose. Ginagamit ng condition: service_healthy clause ang healthcheck na kasama na sa gluetun image, kaya walang magsisimula hangga't hindi nag-uulat ang tunnel na up na ito. Ipinaliliwanag ng Compose healthchecks ang pangkalahatang anyo nito.

Pag-publish sa bawat address sa halip na sa tailnet lamang

Alisin ang address prefix at ang port binds sa 0.0.0.0, na kasama ang public IP ng VPS. Gawin lamang ito kapag nasa likod ito ng firewall na kinokontrol mo, at basahin muna ang ufw note sa itaas.

    ports:
      - "8080:8080/tcp"

Tiyaking dumadaan pa rin ang traffic sa tunnel

Patakbuhin ang parehong request nang dalawang beses: isang beses mula sa loob ng namespace at isang beses mula sa host. Pagkatapos, paghambingin ang mga resulta.

docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org
curl -s https://api.ipify.org

Dapat unang ipakita ang exit address ng VPN provider mo. Dapat namang ipakita ng pangalawa ang address ng VPS. Kung magkapareho ang mga ito, hindi dumadaan sa tunnel ang traffic ng container. Hindi magiging mahalaga ang iba pang fix sa gabay na ito hangga’t hindi ito naitatama.

Ipinapakita ng routing table kung aling traffic ang dumadaan sa tunnel at kung alin ang hindi.

docker run --rm --network=container:gluetun alpine:3.22 ip route show

Dapat tumuro ang default route sa tunnel interface na tun0. Sa ibaba nito, dapat ay may isang route para sa bawat entry sa FIREWALL_OUTBOUND_SUBNETS, na tumuturo sa Docker bridge gateway. Anumang ibang route na lumalabas sa pamamagitan ng eth0 ay traffic na lumalampas sa VPN.

Iniuulat ng control server ng Gluetun ang parehong public IP sa port 8000, sa /v1/publicip/ip. Sa mga bagong bersyon, kailangan mong mag-configure ng authentication para sa mga route ng control server, kaya i-set up muna ito bago ito gamitin bilang dependency.

Ang Isang Maling Subnet ay Lumilikha ng Leak

Ang FIREWALL_OUTBOUND_SUBNETS ay butas na sinasadyang buksan sa firewall, kaya ang laki ng butas ang siya ring laki ng panganib. Narito ang apat na paraan para maging masyadong malaki ito:

  • Ipinapadala ng 0.0.0.0/0 ang lahat ng traffic palabas ng tunnel. Nahuhuli ito ng dalawang IP check sa itaas sa unang run, dahil pareho ang address na ibinabalik ng mga ito.
  • Mas malawak na range kaysa sa target. Kapag binuksan ang 10.0.0.0/8 para maabot ang isang machine sa 10.0.1.7, binubuksan din nito ang bawat address na maaaring i-advertise ng isang torrent peer sa range na iyon. Isulat ang 10.0.1.7/32.
  • Range na sumasaklaw sa sariling mga address ng tunnel. Nagbababala ang gluetun documentation na dahil dito, ipinapadala ng gluetun ang VPN traffic palabas sa bridge sa halip, kaya nasisira ang port forwarding. Suriin ang value ng WIREGUARD_ADDRESSES bago magbukas ng anumang private range.
  • 100.64.0.0/10 para sa Tailscale. Humigit-kumulang apat na milyong address ang binubuksan nito para maabot ang isang peer. Ilagay ang mga peer na kailangan mo bilang mga entry na /32.

Tandaan na saklaw ng setting ang buong namespace. Kapag nagbukas ka ng subnet para maabot ng indexer ang isang host service, binubuksan din ang parehong subnet para sa torrent client na gumagamit ng namespace na iyon. Patakbuhin muli ang public IP check pagkatapos ng bawat pagbabago sa variable na ito, dahil ito lamang ang test na nagpapakita kung naisagawa ng pagbabago ang iyong itinakda.

Ano ang nasisira kapag ni-restart mo ang gluetun

Ang gluetun ang nagmamay-ari ng namespace, kaya ang lifecycle ng gluetun ang lifecycle din ng namespace. Kapag nagsimula ka ng dependent container habang down ang gluetun, agad itong magfa-fail:

Error response from daemon: cannot join network of a non running container

Mas tahimik ang failure kapag ni-restart ang gluetun in place. Patuloy na tumatakbo ang dependent containers habang nire-rebuild sa ilalim ng mga ito ang namespace na kanilang kinapitan, kaya iniuulat ng docker ps na healthy ang lahat kahit walang sumasagot. Pagkatapos ng anumang pagbabago sa gluetun service, i-recreate ang buong group sa halip na isang bahagi lang nito ang i-restart.

docker compose up -d --force-recreate

Ganoon din ang nalalapat sa image updates. Kapag nag-pull ka ng bagong gluetun image at ni-recreate lamang ang service na iyon, maiiwan ang iba pang service na nakaturo sa namespace na wala na.

FAQ

Bakit sinasabi ng Docker na “port publishing and the container type network mode”?

Dahil may ports: block pa rin sa isang service na nagse-set din ng network_mode: service:gluetun. Ang pag-publish ng port ay nagdaragdag ng NAT rule na nagfo-forward ng host port papunta sa sariling network namespace ng container. Walang sariling network namespace ang container sa mode na ito. Burahin ang ports: block sa service na iyon at idagdag ang kaparehong mapping sa gluetun service. Hindi nagbabago ang port number dahil nakikinig pa rin dito ang application sa shared namespace.

Paano naa-access ng ibang container ang service na nasa likod ng gluetun?

Ang mga container sa loob ng parehong namespace ay nagkaka-access sa isa’t isa sa 127.0.0.1. Ang mga container sa labas nito ay gumagamit ng pangalan ng gluetun service, kaya gumagana ang http://gluetun:8080 samantalang hindi gumagana ang http://qbittorrent:8080. Walang address ang application container sa anumang Docker network, kaya walang maire-resolve ang embedded DNS server para sa pangalan nito. Hindi kailangan ang port publishing para rito, basta parehong nasa isang Compose network ang dalawang container.

Ano ang dapat ilagay sa FIREWALL_OUTBOUND_SUBNETS?

Ilagay lamang ang mga address na kailangang konektahan ng container sa likod ng gluetun, at isulat ang mga ito sa pinakamakitid na saklaw na posible. Ang isang machine ay isang /32. Ang dalawang karaniwang entry ay ang Docker host sa 172.17.0.1/32 at isang /32 para sa bawat Tailscale peer na kokonektahan mo. Huwag kailanman idagdag ang 0.0.0.0/0, at huwag magdagdag ng range na nag-o-overlap sa sarili mong tunnel addresses ng VPN. Walang entry rito na kailangan para sa mga inbound connection sa isang published port.

Bakit hindi ma-resolve ng container ang mga pangalan ng Tailscale MagicDNS?

Gumagana ang MagicDNS sa pamamagitan ng pagtuturo sa resolver ng host na gamitin ang DNS server ng Tailscale, pero hindi ginagamit ng container ang resolver ng host. Ginagamit nito ang nakasaad sa sarili nitong /etc/resolv.conf; kapag nasa likod ito ng gluetun, ginagamit nito ang DNS setup ng gluetun. Kumpirmahin ito gamit ang docker exec <container> cat /etc/resolv.conf. Gamitin ang numeric 100.x address ng peer, o i-pin ang pangalan gamit ang extra_hosts entry sa container na iyon.

Paano ko makukumpirma na dumadaan pa rin sa VPN ang traffic?

Magpatakbo ng isang request mula sa loob ng namespace at ng parehong request mula sa host, pagkatapos ay ihambing ang mga sagot. Dapat ibalik ng docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org ang exit address ng VPN provider mo, habang dapat ibalik ng curl -s https://api.ipify.org sa VPS ang address ng VPS. Kapag magkapareho ang dalawang sagot, hindi dinadala ng tunnel ang traffic ng container. Ulitin ang check na ito pagkatapos ng bawat pagbabago sa FIREWALL_OUTBOUND_SUBNETS.