SSD Nodes Learn 🎉 VPS mulai $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-13

Cara Merutekan Container Docker melalui VPN

Port container hilang saat memakai Gluetun karena namespace jaringan bersama. Pahami penyebabnya, error yang muncul, dan cara publikasi port di Docker Compose v3.41.3.

Mengapa port hilang saat Anda merutekan container Docker melalui VPN

Untuk merutekan container Docker melalui VPN, berikan tunnel kepada satu container, lalu hubungkan container lain ke namespace jaringannya dengan network_mode: "service:gluetun". Bagian penghubungan ini sering mengejutkan pengguna. Container yang terhubung tidak lagi memiliki jaringan sendiri. Karena itu, port yang dipublikasikan dan nama service Docker miliknya ikut hilang. Publikasikan port pada container VPN. Container lain dapat mengakses aplikasi melalui nama container VPN.

Jika Anda membiarkan blok ports: pada container yang terhubung, Docker menolak membuat container tersebut:

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

Tool yang digunakan di sini adalah Gluetun. Gluetun adalah container yang terhubung ke penyedia VPN komersial melalui WireGuard atau OpenVPN dan memiliki firewall sendiri. Release v3.41.3 merupakan versi terbaru per August 2026. Contoh ini menggunakan Mullvad dengan WireGuard. Karena itu, Anda memerlukan akun dan key dari penyedia VPN. Jika Anda ingin mengakhiri tunnel pada hardware milik sendiri, menjalankan server WireGuard sendiri pada VPS akan membangun sisi lainnya, sedangkan wg-easy dalam Docker menyediakan antarmuka web untuk konfigurasi tersebut.

Hal yang sebenarnya dilakukan network_mode: "service:gluetun"

Setiap container Docker biasanya mendapatkan namespace jaringan sendiri: interface, tabel routing, aturan firewall, dan socket listening sendiri. Mode service: melewati langkah tersebut dan menjalankan container di dalam namespace gluetun. Satu namespace berarti satu alamat IP, dan hal ini mengubah enam hal.

  • Aplikasi tidak memiliki alamat sendiri. Alamatnya adalah alamat gluetun.
  • Aplikasi tidak terhubung ke jaringan Docker mana pun, sehingga nama servicenya tidak pernah didaftarkan dan tidak pernah dapat di-resolve. Container lain harus menggunakan gluetun.
  • Container di dalam namespace tersebut saling terhubung melalui localhost.
  • Dua container dalam satu namespace tidak dapat melakukan listening pada port yang sama. Dokumentasi Gluetun menyatakan hal ini secara tegas: tidak ada solusi alternatif.
  • Capability dimiliki oleh container, bukan oleh namespace. Gluetun memiliki NET_ADMIN dan /dev/net/tun karena membuat interface tunnel. Container yang terhubung tidak mewarisi capability tersebut.
  • Compose menolak file apa pun jika salah satu service menetapkan network_mode dan networks sekaligus. Hubungkan gluetun ke jaringan Anda, lalu aplikasi akan ikut menggunakannya.

Memulai ulang gluetun akan memutus semua yang terhubung kepadanya. Perilaku ini telah didokumentasikan dan menjadi alasan gluetun memulai ulang proses VPN di dalam container, bukan keluar ketika koneksi gagal. Setelah Anda memulai ulang atau membuat ulang gluetun, mulai ulang container yang terhubung kepadanya.

File 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 adalah rilis stabil terbaru dalam seri v3. Tag :latest menunjuk ke commit terakhir pada branch master, yaitu versi pengembangan, jadi tetapkan :v3 pada mesin yang tidak ingin Anda gunakan untuk melakukan debug secara mendadak.

WEBUI_PORT=8080 harus sama dengan port yang dipublikasikan, karena qBittorrent melakukan bind di dalam namespace gluetun dan aturan publish meneruskan trafik dari host ke port 8080 di namespace tersebut. Jika salah satu angka diubah tanpa mengubah angka lainnya, port tidak akan merespons. 127.0.0.1:8080:8080 mempertahankan antarmuka web pada alamat loopback host. 8080:8080 tanpa alamat akan memublikasikan port pada setiap antarmuka dan membuat aturan firewall sendiri. Inilah penyebab port yang dipublikasikan Docker melewati ufw secara langsung.

Jalankan stack tersebut, lalu periksa dengan urutan berikut:

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

docker compose ps harus menampilkan gluetun sebagai healthy dan qbittorrent sebagai running. Selanjutnya, pastikan alamat keluar dari dalam namespace. Pemeriksaan ini menentukan hasil semua pemeriksaan berikutnya:

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

Kolom ip dalam JSON tersebut harus berisi alamat penyedia VPN Anda. Jika berisi alamat server Anda sendiri, aplikasi tidak berada di dalam tunnel dan semua hal di bawah ini tidak akan berfungsi seperti yang dijelaskan.

Simpan key di luar file compose

gluetun.env menyimpan kredensial dan tidak dimasukkan ke git:

WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
WIREGUARD_ADDRESSES=10.64.222.21/32

Kedua nilai tersebut berasal dari file konfigurasi WireGuard yang Anda buat di area akun provider. Atur mode file ke 600. Jelaskan manfaatnya secara tepat: key tidak masuk ke repository, tetapi docker inspect gluetun tetap menampilkan semua variabel environment kepada siapa pun yang dapat mengakses Docker socket. File environment dan secret di Docker Compose membahas opsi yang lebih aman.

Cara container di luar tunnel berkomunikasi dengan container di dalamnya

Kedua arah tersebut berfungsi, dan masing-masing menggunakan nama yang berbeda. Kedua container memerlukan jaringan Docker bersama, yaitu jaringan gluetun, karena container yang terhubung tidak memiliki jaringan sendiri. Cara jaringan Docker Compose terhubung menjelaskan konfigurasi defaultnya.

Untuk komunikasi dari luar ke dalam, gunakan nama gluetun dan port yang digunakan aplikasi untuk menerima koneksi. Container reverse proxy dapat mengakses antarmuka web qBittorrent di gluetun:8080. Anda tidak memerlukan entri ports: untuk itu karena trafik antar-container tetap berada di jaringan Docker dan tidak pernah melewati port host.

Untuk komunikasi dari dalam ke luar, gunakan nama service container lain, misalnya postgres:5432. Gluetun dapat me-resolve nama container lain dari dalam namespace-nya sejak v3.41. Gunakan versi tersebut atau yang lebih baru jika sebuah nama tidak dapat di-resolve.

Firewall Gluetun menentukan pihak yang boleh membuka koneksi ke Gluetun. Trafik dari jaringan Docker milik gluetun sendiri diizinkan. Klien pada subnet lain, seperti laptop di LAN atau container pada jaringan bridge terpisah, akan dibuang sampai Anda mencantumkan subnet tersebut:

FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24

Makna yang didokumentasikan bersifat spesifik: subnet yang dipisahkan dengan koma dan diizinkan untuk diakses oleh Gluetun serta container yang menggunakan network stack-nya.

Koneksi masuk dari Internet merupakan masalah yang berbeda. Peer milik client torrent datang dari sisi VPN, sehingga mem-publish port 6881 pada host tidak memberikan efek apa pun. Anda memerlukan port yang diteruskan oleh provider, lalu mencantumkan port tersebut di FIREWALL_VPN_INPUT_PORTS. Entri ini mengizinkan port dari sisi server VPN. Inilah bagian yang paling sering dibiarkan tidak berfungsi oleh media stack yang dibuat dengan Docker Compose.

Sakelar pemutus: apa yang terjadi saat tunnel terputus

Pola ini sepadan dengan kompleksitasnya ketika terjadi kegagalan. Container yang terpasang tidak memiliki rute kedua. Satu-satunya jalur keluar dari mesin adalah namespace yang digunakan bersama, sehingga saat tunnel terputus tidak ada jalur cadangan. Firewall Gluetun menerapkan aturan yang sama dari sisi lain: trafik keluar harus melalui tunnel atau menuju endpoint server VPN, sedangkan semua trafik lainnya dibuang. Tidak ada jeda ketika paket dapat keluar melalui interface biasa saat client melakukan koneksi ulang.

Gluetun memantau koneksinya sendiri. Setiap menit, Gluetun mengirim ICMP echo (ping) ke alamat dalam HEALTH_ICMP_TARGET_IPS, yang secara default adalah 1.1.1.1,8.8.8.8. Setiap lima menit, Gluetun membuat koneksi TCP dan TLS (transport layer security) penuh ke HEALTH_TARGET_ADDRESSES, dengan nilai default cloudflare.com:443,github.com:443. Jika pemeriksaan tersebut gagal, Gluetun me-restart VPN di dalam container dan mencatatnya di 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 container yang terpasang dengan mempertimbangkan urutan tersebut. Baris seperti connection refused, operation not permitted, dan i/o timeout di dalam aplikasi merupakan akibat tunnel yang terputus, bukan penyebabnya. Dokumentasi Gluetun menyatakan hal ini secara langsung, karena orang sering melaporkan akibatnya lalu menghabiskan waktu berjam-jam untuk mencari penyebab yang keliru.

HEALTH_RESTART_VPN=on adalah konfigurasi default dan sebaiknya tetap aktif. Nonaktifkan hanya saat men-debug satu kegagalan tertentu, karena jika dinonaktifkan, tunnel yang terputus akan tetap terputus.

Urutan: hentikan stack agar tidak dimulai sebelum tunnel aktif

Image ini menyediakan healthcheck Docker:

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

Perintah tersebut menjalankan salinan gluetun kedua yang berumur singkat. Salinan ini meminta status health dari instance yang sedang berjalan pada http://127.0.0.1:9999/. Tunnel yang berfungsi merespons dengan 200 OK. Tunnel yang bermasalah merespons dengan 500 Internal server error beserta string error, dan container ditandai sebagai tidak sehat setelah satu kegagalan.

condition: service_healthy digunakan untuk menunggu kondisi tersebut. depends_on: [gluetun] biasa hanya menunggu sampai container dimulai. Proses ini terjadi beberapa detik sebelum handshake selesai. Akibatnya, aplikasi dimulai saat jaringan belum berfungsi dan sering menyerah pada percobaan koneksi pertamanya. Healthcheck di Docker Compose menjelaskan sintaks dan kolom waktu tersebut.

Ada satu batasan yang sering terlewat. Compose mengevaluasi kondisi tersebut satu kali, saat membuat container. Compose tidak menghentikan atau memulai ulang aplikasi jika gluetun kemudian menjadi tidak sehat. Auto-healing internal gluetun menangani kasus ini. Karena itu, gluetun memulai ulang proses VPN, bukan container.

Periksa kebocoran DNS sebelum mempercayai konfigurasi

DNS (domain name system) adalah kebocoran yang tetap terjadi meskipun tunnel sudah dikonfigurasi dengan benar. Gluetun menjalankan resolver sendiri di dalam namespace dan meneruskan kueri melalui DoT (DNS over TLS) ke Cloudflare secara default: DNS_UPSTREAM_RESOLVER_TYPE=dot dan DNS_UPSTREAM_RESOLVERS=cloudflare. Biarkan keduanya tanpa perubahan agar pencarian nama terenkripsi dan berlangsung melalui tunnel.

Pengaturan yang menyebabkan masalah ini adalah DNS_UPSTREAM_PLAIN_ADDRESSES. Pengguna biasanya mengaktifkannya ketika suatu nama gagal di-resolve dan mereka ingin router atau resolver milik provider menjawab kueri tersebut. Dokumentasi Gluetun menjelaskan dampaknya secara langsung: seluruh trafik DNS tidak akan melewati tunnel VPN dan akan bocor ke luar tunnel. Trafik Anda tetap privat. Daftar hostname Anda tidak. Kesalahan yang sama pada versi WireGuard dibahas dalam DNS yang berhenti di-resolve melalui tunnel WireGuard.

Untuk mengujinya, tetapkan HTTPPROXY=on pada gluetun dan publish 8888:8888/tcp, lalu arahkan browser ke proxy tersebut dan buka pengujian kebocoran DNS. Hasilnya harus mencantumkan provider Anda atau Cloudflare, bukan router rumah Anda. Dokumentasi Gluetun juga memperingatkan bahwa beberapa pengujian kebocoran dapat menampilkan hasil yang tidak biasa karena resolver di dalam namespace adalah perantara caching lokal, bukan server yang akhirnya memberikan jawaban. Anggap negara yang salah atau resolver ISP Anda sendiri sebagai indikator kebocoran yang sebenarnya.

Menambahkan Tailscale di samping VPN sidecar, dan menentukan mana yang berlaku

Tailscale adalah jaringan overlay yang dibangun di atas WireGuard untuk mengakses mesin milik Anda. Banyak orang menjalankannya di samping VPN provider agar tetap memiliki jalur administratif ke dalam stack. Keduanya jarang saling bertentangan karena alasan yang perlu dipahami. Dokumentasi Tailscale menyatakan perilaku defaultnya: Tailscale berfungsi sebagai jaringan overlay, hanya merutekan trafik antara perangkat yang menjalankan Tailscale, dan tidak memengaruhi trafik Internet publik Anda.

Jadi, jawabannya bergantung pada satu pengaturan.

  • Tailscale dalam container sendiri dengan konfigurasi default: Tailscale tidak pernah melihat trafik keluar aplikasi. Gluetun membawa seluruh trafik tersebut. Tailscale mengakses aplikasi melalui gluetun:8080, sama seperti container eksternal lainnya.
  • Tailscale dipasang ke namespace milik gluetun dengan network_mode: "service:gluetun": Tailscale memerlukan cap_add sendiri untuk net_admin dan net_raw, karena capability tidak ikut terbawa bersama namespace. Dalam mode userspace networking default, TS_USERSPACE aktif, tailscaled tidak membuat interface sama sekali dan bekerja sebagai proxy SOCKS5 atau HTTP, sehingga tidak dapat mengubah routing. Gluetun tetap membawa seluruh trafik.
  • Konfigurasi yang sama dengan TS_USERSPACE=false: tailscaled membuat perangkat tunnel dan memasang route, tetapi hanya untuk rentang tailnet 100.64.0.0/10 serta route subnet apa pun yang Anda iklankan dengan TS_ROUTES. Trafik publik tetap keluar melalui gluetun.
  • Salah satu konfigurasi di atas dengan exit node yang dipilih, sudo tailscale set --exit-node=<exit-node-ip>: Tailscale mengambil alih default route. Tailscale menjadi pemiliknya. Jangan gabungkan konfigurasi ini dengan gluetun. Satu default route hanya boleh memiliki satu pemilik.

Salah satu dampaknya terlihat ketika Tailscale berjalan di dalam tunnel. Peer Tailscale akan melihat alamat milik VPN provider, sehingga koneksi lebih sering beralih ke relay. tailscale status menampilkan relay "..." di samping peer, bukan direct, jika hal itu terjadi. Koneksi tetap berfungsi, tetapi lebih lambat. Jika overlay adalah satu-satunya hal yang benar-benar Anda perlukan, perbedaan antara WireGuard biasa dan Tailscale adalah titik awal yang lebih baik.

Hal yang gagal dan pesan yang akan Anda lihat

Docker menolak membuat container aplikasi. Error response from daemon: conflicting options: port publishing and the container type network mode berarti blok ports: masih terpasang pada service yang terhubung. Pindahkan blok tersebut ke gluetun.

Compose menolak seluruh file. Sebuah service tidak dapat menetapkan network_mode dan networks sekaligus. Letakkan network pada gluetun.

Container lain tidak dapat menemukan aplikasi. curl: (6) Could not resolve host: qbittorrent adalah perilaku yang benar karena container yang terhubung tidak bergabung dengan network mana pun dan tidak mendaftarkan nama. Gunakan gluetun dan port tersebut.

Container terhubung kedua tidak mau start. Dua proses dalam satu namespace tidak dapat melakukan bind pada port yang sama. Proses yang kalah akan melaporkan bahwa alamat tersebut sudah digunakan. Ubah port internal aplikasi atau jalankan gluetun kedua.

Aplikasi tidak memiliki network setelah Anda mengubah gluetun. Me-restart atau membuat ulang gluetun akan memutus konektivitas semua container yang terhubung kepadanya. Restart container-container tersebut.

Halaman kecil dapat dimuat, tetapi halaman besar macet. Itu adalah masalah MTU (maximum transmission unit). Tunnel menambahkan overhead, lalu suatu perangkat dalam jalur membuang paket yang terlalu besar tanpa mengirimkan error. Turunkan WIREGUARD_MTU, coba 1400, lalu 1320.

Gluetun tidak pernah menjadi healthy. Pemeriksaan startup menyebutkan tersangka pertama: WARN [vpn] restarting VPN because it failed to pass the healthcheck: startup check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout. Periksa apakah key sudah kedaluwarsa, kemudian periksa apakah daftar server sudah usang, lalu pastikan firewall host tidak memblokir UDP keluar.

FAQ

Mengapa port yang dipublikasikan oleh container saya berhenti berfungsi di belakang Gluetun?

Karena network_mode: "service:gluetun" menempatkan container ke dalam network namespace milik gluetun, sedangkan satu namespace memiliki satu alamat IP dan satu kumpulan port yang listening. Aplikasi tetap listening, tetapi aturan publish harus berada pada container yang memiliki namespace tersebut. Pindahkan daftar ports: ke service gluetun. Jika Anda membiarkannya pada service yang terhubung, Docker bahkan tidak akan membuatnya: Error response from daemon: conflicting options: port publishing and the container type network mode.

Bagaimana cara mengakses container di dalam tunnel VPN dari container di luarnya?

Gunakan nama service gluetun dan port yang digunakan aplikasi untuk listening, misalnya gluetun:8080. Container yang terhubung tidak memiliki jaringan Docker sendiri, sehingga namanya tidak pernah dapat di-resolve. Tidak ada port yang perlu dipublikasikan untuk trafik antarkontainer. Sebaliknya, container di dalam namespace dapat mengakses container di luar namespace berdasarkan nama servicenya, seperti postgres:5432, pada Gluetun v3.41 dan yang lebih baru. Klien pada subnet yang berbeda, seperti laptop di LAN Anda, akan diblokir oleh firewall gluetun sampai subnet tersebut ditambahkan ke FIREWALL_OUTBOUND_SUBNETS.

Apakah Gluetun berfungsi sebagai kill switch saat VPN terputus?

Ya, karena dua alasan sekaligus. Container yang terhubung tidak memiliki rute selain rute di namespace bersama, sehingga tunnel yang terputus membuatnya tidak memiliki jalur untuk keluar dari mesin. Firewall Gluetun juga hanya mengizinkan trafik keluar melalui tunnel dan menuju endpoint server VPN. Gluetun kemudian me-restart VPN secara internal dan mencatat WARN [vpn] restarting VPN because it failed to pass the healthcheck, bukan keluar, karena setiap container yang terhubung akan kehilangan jaringannya saat gluetun sendiri di-restart.

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

Gluetun, dalam setiap konfigurasi kecuali satu. Secara default, Tailscale hanya merutekan trafik antarperangkat dalam tailnet Anda dan membiarkan trafik publik berjalan langsung. Dalam mode userspace default pada image container, Tailscale tidak membuat interface sama sekali, sehingga tidak dapat memengaruhi routing. Dengan TS_USERSPACE=false, Tailscale hanya memasang rute untuk 100.64.0.0/10 dan subnet yang Anda iklankan. Pengecualiannya adalah exit node: sudo tailscale set --exit-node=<exit-node-ip> menjadikan Tailscale sebagai rute default, sehingga Tailscale yang digunakan. Pilih satu produk untuk mengelola rute default, bukan menumpuk keduanya.