SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-30

Cara Halakan Kontena Docker Melalui VPN Dengan Gluetun

Apabila anda menggunakan network_mode service, port kontena akan hilang. Ketahui sebab namespace rangkaian dikongsi dan lihat contoh fail compose yang berfungsi sepenuhnya.

Mengapa port hilang apabila anda menghalakan kontena Docker melalui VPN

Untuk menghalakan kontena Docker melalui VPN, anda perlu memberikan terowong kepada satu kontena, kemudian menyambungkan kontena lain ke namespace rangkaiannya menggunakan network_mode: "service:gluetun". Proses penyambungan ini sering mengejutkan pengguna. Kontena yang disambungkan tidak lagi mempunyai rangkaiannya sendiri, jadi port yang diterbitkan dan nama servis Docker miliknya akan hilang. Terbitkan port pada kontena VPN sebaliknya, dan kontena lain akan mencapai aplikasi tersebut menggunakan nama kontena VPN berkenaan.

Tinggalkan blok ports: pada kontena yang disambungkan dan Docker akan menolak untuk menciptanya sama sekali:

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

Alat yang digunakan di sini ialah Gluetun, iaitu kontena yang bersambung dengan pembekal VPN (virtual private network) komersial melalui WireGuard atau OpenVPN dan membawa firewallnya sendiri. Release v3.41.3 adalah versi semasa setakat Ogos 2026. Contoh-contoh ini menggunakan Mullvad dengan WireGuard, jadi anda memerlukan akaun dan kunci daripada pembekal anda. Jika anda lebih suka menamatkan terowong pada perkakasan milik anda sendiri, menjalankan pelayan WireGuard anda sendiri pada VPS membina hujung satu lagi, dan wg-easy dalam Docker membungkusnya dalam antara muka web.

Fungsi sebenar network_mode: "service:gluetun"

Setiap bekas Docker biasanya mendapat namespace rangkaiannya sendiri: antara muka, jadual penghalaan, peraturan firewall dan soket pendengar yang tersendiri. Mod service: melangkau langkah tersebut dan memulakan bekas di dalam namespace gluetun. Satu namespace bermakna satu alamat IP, dan ini mengubah enam perkara.

  • Aplikasi tidak mempunyai alamatnya sendiri. Alamatnya ialah alamat gluetun.
  • Aplikasi tidak disambungkan kepada mana-mana rangkaian Docker, jadi nama servisnya tidak pernah didaftarkan dan tidak pernah diselesaikan (resolve). Bekas lain mesti menggunakan gluetun.
  • Bekas di dalam namespace berhubung antara satu sama lain melalui localhost.
  • Dua bekas dalam satu namespace tidak boleh mendengar pada port yang sama. Dokumentasi Gluetun menyatakan perkara ini dengan jelas: tiada jalan penyelesaian.
  • Keupayaan (capabilities) adalah milik bekas, bukan namespace. Gluetun memegang NET_ADMIN dan /dev/net/tun kerana ia mencipta antara muka terowong. Bekas yang disambungkan tidak mewarisinya.
  • Compose menolak sebarang fail di mana satu servis menetapkan kedua-dua network_mode dan networks. Sambungkan gluetun kepada rangkaian anda, dan aplikasi akan turut serta.

Memulakan semula gluetun akan memutuskan sambungan semua yang disambungkan kepadanya. Itu adalah tingkah laku yang didokumentasikan, dan itulah sebabnya gluetun memulakan semula proses VPN di dalam bekas dan bukannya keluar apabila sambungan gagal. Selepas anda memulakan semula atau mencipta semula gluetun sendiri, mulakan semula bekas yang disambungkan kepadanya.

Fail compose yang berfungsi

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

Tag :v3 merupakan keluaran stabil terbaharu dalam siri v3. Tag :latest merujuk kepada commit terakhir bagi branch master, iaitu versi pembangunan terkini, jadi jangan tetapkan :v3 pada mesin yang anda tidak mahu lakukan penyahpepijatan pada hari Selasa.

WEBUI_PORT=8080 perlu sepadan dengan port yang diterbitkan, kerana qBittorrent terikat di dalam namespace gluetun dan peraturan penerbitan menghantar trafik hos ke port 8080 di sana. Jika anda menukar satu nombor tanpa menukar nombor yang satu lagi, port tersebut tidak akan memberikan sebarang respons. 127.0.0.1:8080:8080 mengekalkan antara muka web pada alamat loopback hos. Penggunaan 8080:8080 secara terus akan menerbitkan pada setiap antara muka dan menulis peraturan firewallnya sendiri, yang merupakan punca port yang diterbitkan Docker terlepas terus daripada ufw.

Jalankan servis, kemudian semak mengikut urutan ini:

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

docker compose ps sepatutnya menunjukkan gluetun sebagai healthy dan qbittorrent sebagai running. Kemudian, sahkan alamat keluar dari dalam namespace tersebut; ini adalah semakan yang menentukan segala-galanya:

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

Medan ip dalam JSON tersebut sepatutnya memaparkan alamat pembekal VPN anda. Jika ia memaparkan alamat pelayan anda sendiri, aplikasi tersebut tidak berada di dalam tunnel, dan tiada apa-apa di bawah ini akan berfungsi seperti yang diterangkan.

Simpan kunci di luar fail compose

gluetun.env menyimpan kelayakan, dan ia tidak dimasukkan ke dalam git:

WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
WIREGUARD_ADDRESSES=10.64.222.21/32

Kedua-dua nilai tersebut diperoleh daripada fail konfigurasi WireGuard yang anda jana dalam kawasan akaun pembekal anda. Tetapkan fail tersebut kepada mod 600. Bersikap jujur tentang faedah yang anda peroleh: kunci tersebut tidak berada dalam repositori anda, namun docker inspect gluetun masih mencetak setiap pemboleh ubah persekitaran kepada sesiapa sahaja yang boleh mencapai Docker socket. Fail persekitaran dan rahsia dalam Docker Compose merangkumi pilihan yang lebih kukuh.

Cara kontena di luar terowong berhubung dengan kontena di dalamnya

Kedua-dua arah berfungsi, dan setiap satu menggunakan nama yang berbeza. Kedua-dua kontena memerlukan rangkaian Docker yang dikongsi, iaitu rangkaian gluetun, memandangkan kontena yang dilampirkan tidak mempunyai rangkaiannya sendiri. Cara rangkaian Docker Compose dihubungkan merangkumi tetapan lalai.

Dari luar ke dalam, gunakan nama gluetun dan port yang didengar oleh aplikasi tersebut. Kontena reverse proxy mencapai antara muka web qBittorrent pada gluetun:8080. Tiada entri ports: diperlukan untuk itu, kerana trafik antara kontena kekal dalam rangkaian Docker dan tidak pernah menyentuh port hos.

Dari dalam ke luar, gunakan nama servis kontena yang satu lagi, contohnya postgres:5432. Gluetun telah menyelesaikan nama kontena lain dari dalam namespace-nya sejak v3.41, jadi tetapkan versi tersebut atau yang lebih baharu jika nama gagal diselesaikan.

Firewall gluetun menentukan siapa yang boleh membuka sambungan kepadanya. Trafik daripada rangkaian Docker gluetun sendiri dibenarkan. Klien pada subnet yang berbeza, komputer riba pada LAN anda atau kontena pada rangkaian bridge yang berasingan, akan digugurkan sehingga anda menamakan subnet tersebut:

FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24

Maksud yang didokumentasikan adalah tepat: subnet yang dipisahkan dengan koma yang dibenarkan untuk diakses oleh Gluetun dan kontena yang berkongsi stack rangkaiannya.

Sambungan masuk dari internet adalah masalah yang berasingan. Rakan setara (peers) klien torrent tiba dari sisi VPN, jadi menerbitkan port 6881 pada hos tidak memberi kesan kepada mereka. Anda memerlukan port yang diforward daripada penyedia anda, dan port tersebut perlu disenaraikan dalam FIREWALL_VPN_INPUT_PORTS, yang membenarkan port dari sisi pelayan VPN. Ini adalah bahagian yang kebanyakannya media stack yang dibina dengan Docker Compose biarkan rosak.

Kill switch: apa yang berlaku apabila tunnel terputus

Corak ini menjadi kompleks apabila berlaku kegagalan. Kontena yang dilampirkan tidak mempunyai laluan kedua. Satu-satunya laluan keluar dari mesin tersebut adalah melalui namespace yang dikongsi, jadi apabila tunnel terputus, tiada laluan sandaran. Firewall Gluetun menguatkuasakan peraturan yang sama dari sisi bertentangan: trafik keluar mesti melalui tunnel atau ke endpoint pelayan VPN, dan semua trafik lain akan digugurkan. Tiada tempoh masa di mana paket boleh terlepas keluar melalui antara muka biasa semasa klien menyambung semula.

Gluetun memantau sambungannya sendiri. Setiap minit, ia menghantar ICMP echo (ping) ke alamat dalam HEALTH_ICMP_TARGET_IPS, yang secara lalainya adalah 1.1.1.1,8.8.8.8. Setiap lima minit, ia melakukan dial TCP dan TLS (transport layer security) penuh ke HEALTH_TARGET_ADDRESSES, yang secara lalainya adalah cloudflare.com:443,github.com:443. Apabila ujian ini gagal, ia akan memulakan semula VPN di dalam kontena dan merekodkannya dalam log:

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

Baca log kontena yang dilampirkan dengan mengambil kira urutan tersebut. Baris seperti connection refused, operation not permitted dan i/o timeout di dalam aplikasi adalah kesan daripada tunnel yang mati, bukannya punca. Dokumentasi Gluetun menyatakan perkara ini secara jelas, kerana pengguna sering melaporkan kesan tersebut dan menghabiskan masa berjam-jam untuk menyiasatnya.

HEALTH_RESTART_VPN=on adalah tetapan lalai dan harus dibiarkan aktif. Matikannya hanya semasa anda sedang menyahpepijat kegagalan tertentu, kerana jika ia dimatikan, tunnel yang mati akan kekal mati.

Susunan: menghentikan stack daripada bermula sebelum tunnel aktif

Imej ini menyertakan healthcheck Docker:

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

Perintah tersebut menjalankan salinan gluetun kedua yang berjangka pendek, yang menanyakan pelayan kesihatan bagi gluetun yang sedang berjalan pada http://127.0.0.1:9999/. Tunnel yang berfungsi akan menjawab 200 OK. Tunnel yang rosak akan menjawab 500 Internal server error berserta rentetan ralat, dan kontena tersebut ditandakan sebagai tidak sihat selepas satu kegagalan.

condition: service_healthy ialah perkara yang menunggu untuk itu. depends_on: [gluetun] biasa hanya menunggu kontena untuk bermula, yang berlaku beberapa saat sebelum jabat tangan (handshake) selesai, jadi aplikasi bermula dalam rangkaian yang mati dan sering berputus asa pada percubaan sambungan pertamanya. Healthcheck dalam Docker Compose membincangkan sintaks dan medan masa tersebut.

Satu had sering memerangkap pengguna. Compose menilai syarat tersebut sekali sahaja, semasa ia mencipta kontena. Ia tidak menghentikan atau memulakan semula aplikasi kemudian jika gluetun menjadi tidak sihat. Pemulihan automatik dalaman gluetun sebaliknya menangani kes tersebut, itulah sebabnya ia memulakan semula proses VPN dan bukannya kontena.

Semak kebocoran DNS sebelum anda mempercayai persediaan tersebut

DNS (domain name system) merupakan kebocoran yang tetap berlaku walaupun terowong telah dikonfigurasikan dengan betul. Gluetun menjalankan resolvernya sendiri di dalam namespace dan memajukan pertanyaan melalui DoT (DNS over TLS) ke Cloudflare secara lalai: DNS_UPSTREAM_RESOLVER_TYPE=dot dan DNS_UPSTREAM_RESOLVERS=cloudflare. Biarkan kedua-duanya tanpa diubah supaya carian anda disulitkan dan melalui terowong tersebut.

Tetapan yang menyebabkan masalah ini ialah DNS_UPSTREAM_PLAIN_ADDRESSES. Pengguna cenderung memilih tetapan ini apabila sesuatu nama gagal diselesaikan dan mereka mahu penghala atau resolver pembekal mereka yang menjawab pertanyaan tersebut. Dokumentasi Gluetun menyatakan kosnya dengan jelas: semua trafik DNS tidak akan melalui terowong VPN dan akan bocor keluar daripadanya. Trafik anda kekal peribadi. Senarai nama hos anda tidak. Versi WireGuard bagi kesilapan yang sama dibincangkan dalam DNS yang berhenti menyelesaikan nama melalui terowong WireGuard.

Untuk mengujinya, tetapkan HTTPPROXY=on pada gluetun dan terbitkan 8888:8888/tcp, kemudian halakan pelayar ke proksi tersebut dan muatkan ujian kebocoran DNS. Hasilnya harus memaparkan nama pembekal anda atau Cloudflare, bukan penghala rumah anda. Dokumentasi Gluetun sendiri memberi amaran bahawa sesetengah ujian kebocoran melaporkan hasil yang ganjil, kerana resolver di dalam namespace merupakan perantara caching tempatan dan bukannya pelayan yang menjawab pertanyaan akhir. Anggap negara yang salah atau resolver ISP anda sendiri sebagai petunjuk sebenar.

Menambah Tailscale di samping sidecar VPN, dan yang mana satu akan menang

Tailscale ialah rangkaian tindanan (overlay network) yang dibina berasaskan WireGuard untuk mencapai mesin anda sendiri, dan pengguna sering menjalankannya di samping VPN pembekal untuk mengekalkan laluan pentadbiran ke dalam tindanan tersebut. Kedua-duanya jarang bertembung, atas sebab yang perlu difahami. Dokumentasi Tailscale menyatakan tetapan lalai: ia bertindak sebagai rangkaian tindanan, ia hanya menghalakan trafik antara peranti yang menjalankan Tailscale, dan ia tidak menyentuh trafik internet awam anda.

Jadi jawapannya bergantung pada satu tetapan.

  • Tailscale dalam kontena sendiri, konfigurasi lalai: ia tidak pernah melihat trafik keluar aplikasi. Gluetun membawa kesemuanya. Tailscale mencapai aplikasi pada gluetun:8080, sama seperti mana-mana kontena luar yang lain.
  • Tailscale dilampirkan pada namespace gluetun dengan network_mode: "service:gluetun": ia memerlukan cap_add sendiri bagi net_admin dan net_raw, kerana keupayaan (capabilities) tidak disertakan bersama namespace tersebut. Dalam mod rangkaian ruang pengguna (userspace) lalai, TS_USERSPACE dihidupkan, tailscaled tidak mencipta sebarang antara muka dan berfungsi sebagai proksi SOCKS5 atau HTTP, jadi ia tidak boleh mengubah penghalaan. Gluetun masih membawa segala-galanya.
  • Perkara yang sama, dengan TS_USERSPACE=false: tailscaled mencipta peranti terowong dan memasang laluan, tetapi hanya untuk julat tailnet 100.64.0.0/10 serta sebarang laluan subnet yang anda iklankan dengan TS_ROUTES. Trafik awam masih keluar melalui gluetun.
  • Mana-mana perkara di atas dengan exit node dipilih, sudo tailscale set --exit-node=<exit-node-ip>: Tailscale menuntut laluan lalai dan menang. Jangan gabungkan itu dengan gluetun. Satu laluan lalai, satu pemilik.

Jika laluan yang diiklankan itu adalah tujuannya, dan anda mahu seluruh rangkaian peribadi di belakang kotak itu boleh dicapai dan bukannya kotak itu sahaja, menjalankan penghala subnet Tailscale pada VPS merangkumi kelulusan laluan, penghantaran IP dan flag bahagian klien yang TS_ROUTES tidak lakukan secara sendirian.

Jika Tailscale berada di sana untuk memberikan anda URL pentadbir dan bukannya laluan, tailscale serve dan tailscale funnel meletakkan HTTPS di hadapan gluetun:8080 untuk tailnet anda, dengan hanya funnel yang membukanya kepada internet awam.

Satu kesan sampingan kelihatan apabila Tailscale berjalan di dalam terowong. Rakan setaranya melihat alamat pembekal VPN, jadi jangkakan ia akan beralih kepada relay dengan lebih kerap. tailscale status menunjukkan relay "..." di sebelah rakan setara dan bukannya direct apabila perkara itu berlaku. Sambungan tersebut berfungsi dan ia lebih perlahan. Jika rangkaian tindanan adalah satu-satunya perkara yang anda perlukan, perbezaan antara WireGuard biasa dan Tailscale adalah titik permulaan yang lebih baik.

Perkara yang gagal, dan mesej yang akan anda lihat

Docker enggan mencipta bekas aplikasi. Error response from daemon: conflicting options: port publishing and the container type network mode bermaksud blok ports: masih berada pada servis yang dilampirkan. Pindahkan ia ke gluetun.

Compose enggan memproses keseluruhan fail. Sesuatu servis tidak boleh menetapkan network_mode dan networks serentak. Letakkan rangkaian pada gluetun.

Bekas lain tidak dapat menyelesaikan nama aplikasi. curl: (6) Could not resolve host: qbittorrent adalah kelakuan yang betul, kerana bekas yang dilampirkan tidak menyertai sebarang rangkaian dan tidak mendaftarkan sebarang nama. Gunakan gluetun dan port tersebut.

Bekas kedua yang dilampirkan tidak akan bermula. Dua proses dalam satu namespace tidak boleh mengikat port yang sama, dan proses yang gagal akan melaporkan bahawa alamat tersebut sudah digunakan. Tukar port dalaman aplikasi, atau jalankan gluetun kedua.

Aplikasi tiada rangkaian selepas anda mengubah gluetun. Memulakan semula atau mencipta semula gluetun akan memutuskan sambungan untuk semua yang dilampirkan kepadanya. Mulakan semula bekas-bekas tersebut.

Halaman kecil dimuatkan tetapi halaman besar tergantung. Itu adalah masalah MTU (maximum transmission unit). Terowong menambah overhead, dan sesuatu pada laluan tersebut menggugurkan paket yang terlalu besar tanpa menghantar ralat kembali. Rendahkan WIREGUARD_MTU, cuba 1400, kemudian 1320.

Gluetun tidak pernah menjadi sihat. Pemeriksaan permulaan menamakan suspek utama: WARN [vpn] restarting VPN because it failed to pass the healthcheck: startup check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout. Semak sama ada kunci telah tamat tempoh, kemudian sama ada senarai pelayan sudah lapuk, dan seterusnya sama ada firewall hos anda menyekat UDP keluar.

FAQ

Mengapa port yang diterbitkan oleh kontena saya berhenti berfungsi di sebalik Gluetun?

Kerana network_mode: "service:gluetun" meletakkan kontena tersebut di dalam network namespace milik Gluetun, dan satu namespace hanya mempunyai satu alamat IP serta satu set port yang mendengar (listening ports). Aplikasi tersebut terus mendengar, tetapi peraturan penerbitan (publish rule) perlu berada pada kontena yang memiliki namespace tersebut. Pindahkan senarai ports: ke servis Gluetun. Jika anda membiarkannya pada servis yang dilampirkan, Docker tidak akan menciptanya: Error response from daemon: conflicting options: port publishing and the container type network mode.

Bagaimanakah saya boleh mencapai kontena di dalam terowong VPN daripada kontena di luarnya?

Gunakan nama servis Gluetun dan port yang didengar oleh aplikasi tersebut, contohnya gluetun:8080. Kontena yang dilampirkan tidak berada pada mana-mana rangkaian Docker miliknya sendiri, jadi namanya tidak akan dapat diselesaikan (resolve). Tiada apa-apa yang perlu diterbitkan untuk trafik antara kontena. Bagi arah sebaliknya, kontena di dalam namespace mencapai kontena luar melalui nama servisnya, seperti postgres:5432, pada Gluetun v3.41 dan versi lebih baharu. Pelanggan pada subnet berbeza, seperti komputer riba pada LAN anda, akan digugurkan oleh firewall Gluetun sehingga anda menambah subnet tersebut ke FIREWALL_OUTBOUND_SUBNETS.

Adakah Gluetun berfungsi sebagai kill switch apabila VPN terputus?

Ya, atas dua sebab serentak. Kontena yang dilampirkan tidak mempunyai laluan (route) kecuali laluan dalam namespace yang dikongsi, jadi terowong yang mati menyebabkan ia tidak mempunyai laluan keluar dari mesin tersebut. Firewall Gluetun juga hanya membenarkan trafik keluar melalui terowong dan ke titik akhir (endpoint) pelayan VPN. Gluetun kemudian memulakan semula VPN secara dalaman, dengan mencatat WARN [vpn] restarting VPN because it failed to pass the healthcheck, bukannya keluar (exit), kerana setiap kontena yang dilampirkan akan kehilangan rangkaiannya apabila Gluetun sendiri dimulakan semula.

Tailscale dan Gluetun dalam stack yang sama: yang mana satu membawa trafik keluar?

Gluetun, dalam setiap konfigurasi kecuali satu. Tailscale hanya menghalakan trafik antara peranti dalam tailnet anda secara lalai dan membiarkan trafik awam tidak terganggu. Dalam mod userspace lalai imej kontena, ia tidak mencipta sebarang antara muka (interface), jadi ia tidak boleh menjejaskan penghalaan. Dengan TS_USERSPACE=false, ia hanya memasang laluan untuk 100.64.0.0/10 dan subnet yang anda iklankan. Pengecualiannya ialah exit node: sudo tailscale set --exit-node=<exit-node-ip> menjadikan Tailscale sebagai laluan lalai, dan dalam keadaan itu ia akan mengambil alih. Pilih satu produk untuk menguasai laluan lalai daripada menyusun kedua-duanya.