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

Docker VPN: Bakit Nawawala ang Published Ports

Naka-Gluetun sidecar ba ang container pero nawawala ang ports? Alamin kung bakit shared network namespace ang sanhi, at gamitin ang gumaganang Compose file.

Bakit nawawala ang ports kapag niruruta ang Docker containers sa VPN

Para i-route ang Docker containers sa VPN, ibigay ang tunnel sa isang container, pagkatapos ay i-attach ang iba sa network namespace nito gamit ang network_mode: "service:gluetun". Ang attachment na ito ang madalas nakalilito. Kapag naka-attach na ang isang container, wala na itong sariling network. Kaya nawawala kasama nito ang mga published port at ang Docker service name nito. I-publish ang mga port sa VPN container. Maa-access naman ng ibang containers ang app gamit ang pangalan ng VPN container.

Kung mag-iiwan ka ng ports: block sa naka-attach na container, tatanggi ang Docker na gawin ito:

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

Ang tool dito ay Gluetun, isang container na kumokonekta sa isang commercial VPN (virtual private network) provider gamit ang WireGuard o OpenVPN at may sarili nitong firewall. Ang kasalukuyang release ay v3.41.3 noong August 2026. Gumagamit ang mga halimbawa ng Mullvad at WireGuard, kaya kailangan mo ng account at key mula sa iyong provider. Kung mas gusto mong sa hardware na pagmamay-ari mo i-terminate ang tunnel, ginagawa ng pagpapatakbo ng sarili mong WireGuard server sa isang VPS ang kabilang dulo, habang inilalagay ng wg-easy sa Docker ang configuration na iyon sa isang web interface.

Ano ang aktuwal na ginagawa ng network_mode: "service:gluetun"

Karaniwang may sariling network namespace ang bawat Docker container: sarili nitong interfaces, routing table, firewall rules, at listening sockets. Nilalaktawan ng mode na service: ang hakbang na ito at sinisimulan ang container sa loob ng namespace ng gluetun. Isang IP address ang ginagamit ng isang namespace, kaya nagbabago ang anim na bagay.

  • Walang sariling address ang app. Address ng gluetun ang ginagamit nito.
  • Hindi nakakabit ang app sa anumang Docker network, kaya hindi nirerehistro at hindi nari-resolve ang service name nito. Dapat gamitin ng ibang container ang gluetun.
  • Nakakakonekta ang mga container sa loob ng namespace sa isa't isa gamit ang localhost.
  • Hindi maaaring makinig ang dalawang container sa iisang namespace sa parehong port. Malinaw ang Gluetun documentation tungkol dito: walang workaround.
  • Para sa container ang capabilities, hindi para sa namespace. Hawak ng Gluetun ang NET_ADMIN at /dev/net/tun dahil ito ang gumagawa ng tunnel interface. Hindi namamana ng nakakabit na container ang mga ito.
  • Tinatanggihan ng Compose ang anumang file kung parehong nagse-set ang isang service ng network_mode at networks. Ikabit ang gluetun sa mga network mo, at sasabay dito ang app.

Idinidiskonekta ng pag-restart sa gluetun ang lahat ng nakakabit dito. Nakadokumento ang behavior na ito, at ito ang dahilan kung bakit nire-restart ng gluetun ang VPN process sa loob ng container sa halip na lumabas kapag nabigo ang connection. Pagkatapos mong i-restart o muling gawin ang gluetun, i-restart ang mga container na nakakabit dito.

Ang compose file na gumagana

services:
  gluetun:
    image: qmcgaw/gluetun:v3
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    environment:
      - VPN_SERVICE_PROVIDER=mullvad
      - VPN_TYPE=wireguard
      - SERVER_CITIES=Amsterdam
      - TZ=Europe/Amsterdam
    env_file:
      - ./gluetun.env
    volumes:
      - ./gluetun:/gluetun
    ports:
      - 127.0.0.1:8080:8080/tcp
    restart: unless-stopped

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

Ang :v3 tag ang pinakabagong stable release sa v3 series. Ang :latest tag ay tumutukoy sa huling commit ng master branch, na development edge. Kaya gamitin ang :v3 sa machine na ayaw mong i-debug sa isang ordinaryong araw.

Kailangang tumugma ang WEBUI_PORT=8080 sa published port, dahil nagbi-bind ang qBittorrent sa loob ng gluetun's namespace at ipinapasa ng publish rule ang traffic mula sa host sa port 8080 doon. Kapag binago mo ang isang number nang hindi binabago ang isa pa, walang sasagot sa port. Pinapanatili ng 127.0.0.1:8080:8080 ang web interface sa loopback address ng host. Kapag bare 8080:8080 ang ginamit, ipo-publish nito ang port sa bawat interface at gagawa ito ng sarili nitong firewall rule. Ito ang dahilan kung bakit diretsong nalalampasan ng Docker published ports ang ufw.

I-start ito, pagkatapos ay suriin sa ganitong pagkakasunod-sunod:

docker compose up -d
docker compose ps
docker compose logs gluetun | tail -30

Dapat ipakita ng docker compose ps ang gluetun bilang healthy at ang qbittorrent bilang running. Pagkatapos, kumpirmahin ang exit address mula sa loob ng namespace. Ito ang pagsusuring magpapasya sa lahat ng iba pa:

docker run --rm --network=container:gluetun alpine:3.22 sh -c "apk add wget && wget -qO- https://ipinfo.io"

Ang ip field sa JSON na iyon ay dapat address ng VPN provider mo. Kung sarili mong address ng server ang lumabas, wala ang app sa tunnel at hindi gagana ang mga sumusunod ayon sa inilalarawan.

Huwag ilagay ang mga key sa compose file

Nasa gluetun.env ang mga credential, at hindi ito isinasama sa git:

WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
WIREGUARD_ADDRESSES=10.64.222.21/32

Parehong nagmumula ang mga value sa WireGuard configuration file na ginagawa mo sa account area ng iyong provider. Itakda ang file sa mode 600. Maging malinaw sa pakinabang nito: nananatiling wala ang key sa iyong repository, pero inililista pa rin ng docker inspect gluetun ang lahat ng environment variable sa sinumang makakaabot sa Docker socket. Sinasaklaw ng Mga environment file at secret sa Docker Compose ang mas matibay na mga opsyon.

Paano nakikipag-ugnayan ang container na nasa labas ng tunnel sa container na nasa loob nito

Gumagana ang dalawang direksiyon, at magkaibang pangalan ang ginagamit ng bawat isa. Kailangang nasa iisang shared Docker network ang dalawang container. Ito ang network ng gluetun, dahil walang sariling network ang naka-attach na container. Ipinaliliwanag sa Paano ikinokonekta ang mga Docker Compose network ang mga default.

Mula sa labas papunta sa loob, gamitin ang pangalan ng gluetun at ang port na pakikinggan ng app. Maaabot ng reverse proxy container ang web interface ng qBittorrent sa gluetun:8080. Hindi kailangan ng entry na ports: para rito, dahil nananatili sa Docker network ang traffic sa pagitan ng mga container at hindi ito dumadaan sa host port.

Mula sa loob papunta sa labas, gamitin ang service name ng kabilang container, gaya ng postgres:5432. Nari-resolve ng Gluetun ang mga pangalan ng ibang container mula sa loob ng namespace nito simula v3.41. Gumamit ng bersiyong iyon o mas bago kung hindi nari-resolve ang isang pangalan.

Tinutukoy ng firewall ng Gluetun kung sino ang maaaring magbukas ng koneksyon dito. Pinapayagan ang traffic mula sa sariling Docker network ng gluetun. Ang client mula sa ibang subnet, gaya ng laptop sa iyong LAN o container sa hiwalay na bridge network, ay ibinabagsak hanggang sa pangalanan mo ang subnet na iyon:

FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24

Eksakto ang dokumentadong kahulugan: mga subnet na pinaghihiwalay ng kuwit na pinapayagang mag-access ang Gluetun at ang mga container na nakikibahagi sa network stack nito.

Hiwalay na problema ang mga inbound connection mula sa internet. Mula sa VPN side dumarating ang mga peer ng torrent client, kaya walang silbi ang pag-publish ng port 6881 sa host para sa kanila. Kailangan mo ng forwarded port mula sa iyong provider, at dapat ilista ang port na iyon sa FIREWALL_VPN_INPUT_PORTS, na nagpapahintulot sa mga port mula sa panig ng VPN server. Ito ang bahaging madalas iwanang hindi gumagana ng karamihan sa media stack na binuo gamit ang Docker Compose.

Ang kill switch: ano ang nangyayari kapag bumagsak ang tunnel

Nakikita ang halaga ng pattern na ito kapag may failure. Walang pangalawang route ang naka-attach na container. Ang namespace na pinagsasaluhan nito ang tanging daan palabas ng machine, kaya kapag down ang tunnel, wala itong fallback. Ipinapatupad din ng firewall ng Gluetun ang parehong rule mula sa kabilang panig: dumadaan sa tunnel o sa VPN server endpoint ang outbound traffic, at bina-drop ang lahat ng iba pa. Walang pagkakataong lumabas ang mga packet sa plain interface habang nagre-reconnect ang client.

Minomonitor ng Gluetun ang sarili nitong connection. Bawat minuto, nagpapadala ito ng ICMP echo (isang ping) sa mga address sa HEALTH_ICMP_TARGET_IPS, na ang default ay 1.1.1.1,8.8.8.8. Bawat limang minuto, nagsasagawa ito ng buong TCP at TLS (transport layer security) dial sa HEALTH_TARGET_ADDRESSES, na ang default ay cloudflare.com:443,github.com:443. Kapag nabigo ang mga ito, nire-restart nito ang VPN sa loob ng container at nilala-log ang pangyayari:

WARN [vpn] restarting VPN because it failed to pass the healthcheck: periodic check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout

Basahin ang logs ng naka-attach na container ayon sa pagkakasunod-sunod na ito. Ang mga linyang gaya ng connection refused, operation not permitted, at i/o timeout sa loob ng app ay resulta ng dead tunnel, hindi mga sanhi. Tahasang sinasabi ito sa documentation ng Gluetun dahil iniuulat ng mga tao ang resulta at ilang oras nila itong hinahabol.

Default ang HEALTH_RESTART_VPN=on at dapat itong manatiling naka-on. I-off lamang ito habang nagde-debug ng isang partikular na failure, dahil kapag naka-off ito, mananatiling dead ang dead tunnel.

Pagkakasunod-sunod: pigilan ang pagsisimula ng stack bago maging aktibo ang tunnel

May Docker healthcheck ang image:

HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /gluetun-entrypoint healthcheck

Nagsisimula ang command na iyon ng isa pang panandaliang kopya ng gluetun. Kinokonsulta nito ang health server ng kasalukuyang tumatakbong instance sa http://127.0.0.1:9999/. Sumasagot ang gumaganang tunnel ng 200 OK. Ang may problema ay sumasagot ng 500 Internal server error na may error string, at minamarkahan ang container bilang unhealthy matapos ang isang failure.

Ang condition: service_healthy ang naghihintay nito. Ang karaniwang depends_on: [gluetun] ay naghihintay lamang na magsimula ang container. Nangyayari ito ilang segundo bago makumpleto ang handshake. Dahil dito, nagsisimula ang app habang walang gumaganang network at madalas nitong iniiwan ang unang connection attempt. Ipinapaliwanag ng Healthcheck sa Docker Compose ang syntax at mga timing field.

May isang limitasyon na madalas nakalilito. Isang beses lamang sinusuri ng Compose ang condition na iyon, kapag ginagawa nito ang container. Hindi nito awtomatikong pinatitigil o nire-restart ang app kapag naging unhealthy ang gluetun. Ang internal auto-healing ng gluetun ang humahawak sa sitwasyong iyon. Kaya ang VPN process ang nire-restart nito, hindi ang container.

Suriin ang DNS leak bago pagkatiwalaan ang setup

Ang DNS (domain name system) ang leak na nananatili kahit tama ang tunnel. Gumagamit ang Gluetun ng sarili nitong resolver sa loob ng namespace at ipinapasa nito ang mga query sa DoT (DNS over TLS) patungo sa Cloudflare bilang default: DNS_UPSTREAM_RESOLVER_TYPE=dot at DNS_UPSTREAM_RESOLVERS=cloudflare. Huwag baguhin ang dalawang ito upang ma-encrypt ang iyong lookups at dumaan ang mga ito sa tunnel.

Ang setting na nagdudulot ng problema ay DNS_UPSTREAM_PLAIN_ADDRESSES. Ginagamit ito kapag hindi ma-resolve ang isang pangalan at nais nilang ang router o resolver ng kanilang provider ang sumagot. Malinaw ang nakasaad na kapalit sa dokumentasyon ng Gluetun: hindi dadaan sa VPN tunnel ang lahat ng DNS traffic at magle-leak ito palabas ng tunnel. Mananatiling private ang iyong traffic. Hindi ganoon ang listahan ng iyong mga hostname. Tinalakay ang kaparehong pagkakamali sa WireGuard sa DNS na hindi na nagre-resolve sa WireGuard tunnel.

Para subukan ito, itakda ang HTTPPROXY=on sa gluetun at i-publish ang 8888:8888/tcp, pagkatapos ay ituro ang browser sa proxy na iyon at mag-load ng DNS leak test. Dapat pangalanan ng resulta ang iyong provider o Cloudflare, at hindi kailanman ang home router mo. Nagbababala ang sariling dokumentasyon ng Gluetun na maaaring magpakita ng kakaibang resulta ang ilang leak test dahil ang resolver sa loob ng namespace ay isang local caching intermediary, hindi ang server na aktuwal na nagbibigay ng huling sagot. Ituring na tunay na signal ang maling bansa o ang resolver ng sarili mong ISP.

Pagdaragdag ng Tailscale sa tabi ng VPN sidecar, at kung alin ang mananaig

Ang Tailscale ay isang overlay network na binuo sa WireGuard para ma-access ang sarili mong mga machine. Pinapatakbo ito ng mga user kasabay ng provider VPN upang mapanatili ang admin access papunta sa stack. Bihira silang mag-conflict sa isa't isa dahil may malinaw na dahilan. Ayon sa documentation ng Tailscale, ito ang default na behavior: kumikilos ito bilang overlay network, nagra-route lamang ito ng traffic sa pagitan ng mga device na nagpapatakbo ng Tailscale, at hindi nito hinahawakan ang public internet traffic mo.

Depende ang sagot sa isang setting.

  • Tailscale sa sarili nitong container, gamit ang default configuration: hindi nito kailanman nakikita ang outbound traffic ng app. Gluetun ang nagdadala ng lahat ng traffic na iyon. Ina-access ng Tailscale ang app sa gluetun:8080, gaya ng ibang external container.
  • Naka-attach ang Tailscale sa namespace ng gluetun gamit ang network_mode: "service:gluetun": kailangan nito ng sarili nitong cap_add ng net_admin at net_raw dahil hindi awtomatikong kasama ng namespace ang mga capability. Sa default userspace networking mode, naka-on ang TS_USERSPACE. Walang interface na nililikha ang tailscaled at gumagana ito bilang SOCKS5 o HTTP proxy, kaya hindi nito mababago ang routing. Gluetun pa rin ang nagdadala ng lahat ng traffic.
  • Pareho ito, pero gamit ang TS_USERSPACE=false: lumilikha ang tailscaled ng tunnel device at nag-i-install ng mga route, pero para lamang sa tailnet range na 100.64.0.0/10 at sa anumang subnet route na ina-advertise mo gamit ang TS_ROUTES. Sa gluetun pa rin dumadaan ang public traffic.
  • Anuman sa mga nabanggit, kapag may napiling exit node gamit ang sudo tailscale set --exit-node=<exit-node-ip>: inaangkin ng Tailscale ang default route at ito ang mananaig. Huwag itong isabay sa gluetun. Isang default route, isang may-ari.

Makikita ang isang epekto kapag tumatakbo ang Tailscale sa loob ng tunnel. Nakikita ng mga peer nito ang address ng VPN provider, kaya mas madalas itong gumagamit ng relay. Ipinapakita ng tailscale status ang relay "..." sa tabi ng isang peer sa halip na direct kapag nangyari ito. Gumagana ang connection, pero mas mabagal ito. Kung overlay lang talaga ang kailangan mo, mas magandang panimulang punto ang pagkakaiba ng plain WireGuard at Tailscale.

Ano ang masisira at ang mensaheng makikita mo

Tumanggi ang Docker na likhain ang app container. Ang Error response from daemon: conflicting options: port publishing and the container type network mode ay nangangahulugang may ports: block na nakatalaga pa rin sa naka-attach na service. Ilipat ito sa gluetun.

Tumanggi ang Compose na tanggapin ang buong file. Hindi maaaring magtakda ang isang service ng parehong network_mode at networks. Ilagay ang mga network sa gluetun.

Hindi ma-resolve ng ibang container ang app. Tamang behavior ang curl: (6) Could not resolve host: qbittorrent dahil walang network na sinalihan at walang pangalan na nirehistro ang naka-attach na container. Gamitin ang gluetun at ang port.

Hindi nagsisimula ang ikalawang naka-attach na container. Hindi maaaring mag-bind ang dalawang process sa iisang namespace sa parehong port. Mag-uulat ang natalong process na ginagamit na ang address. Palitan ang internal port ng app, o magpatakbo ng ikalawang gluetun.

Wala nang network ang app matapos mong baguhin ang gluetun. Kapag ni-restart o ni-recreate ang gluetun, nawawala ang connectivity ng lahat ng naka-attach dito. I-restart ang mga container na iyon.

Naglo-load ang maliliit na page pero nagha-hang ang malalaki. MTU (maximum transmission unit) ang sanhi nito. Nagdaragdag ng overhead ang tunnel, at may bahagi ng path na nagda-drop ng mga packet na lampas sa laki nito nang walang ibinabalik na error. Bawasan ang WIREGUARD_MTU, subukan ang 1400, saka ang 1320.

Hindi kailanman nagiging healthy ang Gluetun. Tinutukoy ng startup check ang mga unang dapat suriin: WARN [vpn] restarting VPN because it failed to pass the healthcheck: startup check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout. Tingnan kung expired na ang key, pagkatapos kung luma na ang server list, at kung bina-block ng host firewall ang outbound UDP.

FAQ

Bakit hindi na gumana ang mga published port ng container ko kapag nasa likod ito ng Gluetun?

Dahil inilalagay ng network_mode: "service:gluetun" ang container sa network namespace ng gluetun, at ang isang namespace ay may iisang IP address at iisang set ng listening port. Patuloy na nakikinig ang app, pero dapat nasa container na nagmamay-ari ng namespace ang publish rule. Ilipat ang listahan ng ports: sa gluetun service. Kung iniwan mo ito sa attached service, hindi man lang ito gagawin ng Docker: Error response from daemon: conflicting options: port publishing and the container type network mode.

Paano ko maaabot mula sa container na nasa labas ng VPN tunnel ang container na nasa loob nito?

Gamitin ang service name ng gluetun at ang port na ginagamit ng app sa pakikinig, halimbawa gluetun:8080. Walang sariling Docker network ang attached container, kaya hindi kailanman mare-resolve ang sarili nitong pangalan. Walang kailangang i-publish para sa container-to-container traffic. Sa kabilang direksyon, maaabot ng container na nasa loob ng namespace ang container na nasa labas nito gamit ang service name nito, gaya ng postgres:5432, sa Gluetun v3.41 at mas bagong bersyon. Ang client na nasa ibang subnet, gaya ng laptop sa LAN mo, ay bina-block ng firewall ng gluetun hanggang idagdag mo ang subnet na iyon sa FIREWALL_OUTBOUND_SUBNETS.

Gumagana ba ang Gluetun bilang kill switch kapag naputol ang VPN?

Oo, at dalawang dahilan ang gumagana nang sabay. Walang ibang route ang attached container bukod sa route sa shared namespace, kaya kapag namatay ang tunnel, wala itong path palabas ng machine. Pinapayagan din ng firewall ng Gluetun ang outbound traffic lamang sa tunnel at sa VPN server endpoint. Sa halip na mag-exit, nire-restart ng Gluetun ang VPN sa loob nito at nagla-log ng WARN [vpn] restarting VPN because it failed to pass the healthcheck, dahil mawawalan ng network ang bawat attached container kapag mismong gluetun ang nag-restart.

Tailscale at Gluetun sa iisang stack: alin ang nagdadala ng outbound traffic?

Gluetun, maliban sa isang configuration. Bilang default, niruruta lamang ng Tailscale ang traffic sa pagitan ng mga device sa iyong tailnet at hinahayaan nitong dumaan nang hiwalay ang public traffic. Sa default userspace mode ng container image, wala itong ginagawang interface, kaya hindi nito maaapektuhan ang routing. Kapag ginamit ang TS_USERSPACE=false, nag-i-install ito ng mga route para sa 100.64.0.0/10 at sa mga in-advertise mong subnet lamang. Ang exception ay isang exit node: ginagawa ng sudo tailscale set --exit-node=<exit-node-ip> na default route ang Tailscale, kaya ito ang mananaig. Pumili ng isang product na magmamay-ari ng default route sa halip na pagsabayin ang dalawa.