SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-29

Cara Menghubungkan Docker Container ke VPN via Gluetun

Pelajari alasan port menghilang saat menggunakan network_mode service di Docker. Dapatkan konfigurasi Docker Compose yang tepat untuk menjalankan aplikasi di balik Gluetun.

Mengapa port menghilang saat Anda merutekan container Docker melalui VPN

Untuk merutekan container Docker melalui VPN, Anda memberikan tunnel ke satu container, lalu menghubungkan container lainnya ke namespace jaringan container tersebut dengan network_mode: "service:gluetun". Proses penghubungan inilah yang sering mengejutkan pengguna. Container yang dihubungkan tidak lagi memiliki jaringan sendiri, sehingga port yang dipublikasikan dan nama service Docker-nya ikut menghilang. Publikasikan port pada container VPN sebagai gantinya, dan container lain akan menjangkau aplikasi tersebut melalui nama container VPN.

Jika Anda menyertakan blok ports: pada container yang dihubungkan, Docker akan menolak untuk membuatnya sama sekali:

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

Alat yang digunakan di sini adalah Gluetun, sebuah container yang terhubung ke penyedia VPN (virtual private network) komersial melalui WireGuard atau OpenVPN dan memiliki firewall sendiri. Release v3.41.3 adalah versi terkini per Agustus 2026. Contoh-contoh ini menggunakan Mullvad dengan WireGuard, jadi Anda memerlukan akun dan kunci dari penyedia Anda. Jika Anda lebih memilih untuk melakukan terminasi tunnel pada perangkat keras milik sendiri, menjalankan server WireGuard sendiri di VPS akan membangun sisi lainnya, dan wg-easy di Docker membungkusnya dalam antarmuka web.

Apa yang sebenarnya dilakukan network_mode: "service:gluetun"

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

  • Aplikasi tidak memiliki alamat sendiri. Alamatnya adalah alamat milik gluetun.
  • Aplikasi tidak terhubung ke jaringan Docker mana pun, sehingga nama servicenya tidak pernah terdaftar dan tidak pernah ter-resolve. Container lain harus menggunakan gluetun.
  • Container di dalam namespace yang sama saling terhubung melalui localhost.
  • Dua container dalam satu namespace tidak dapat melakukan listening pada port yang sama. Dokumentasi Gluetun menyatakan hal ini dengan tegas: tidak ada solusi untuk masalah ini.
  • Capabilities dimiliki oleh container, bukan oleh namespace. Gluetun memegang NET_ADMIN dan /dev/net/tun karena ia membuat interface tunnel. Container yang terpasang tidak mewarisi kemampuan tersebut.
  • Compose akan menolak file apa pun di mana satu service mengatur network_mode dan networks secara bersamaan. Pasang gluetun ke jaringan Anda, dan aplikasi akan ikut serta di dalamnya.

Melakukan restart pada gluetun akan memutuskan koneksi semua yang terpasang padanya. Hal ini merupakan perilaku yang terdokumentasi, dan itulah alasan mengapa gluetun melakukan restart pada proses VPN di dalam container alih-alih keluar saat koneksi gagal. Setelah Anda melakukan restart atau membuat ulang gluetun secara manual, lakukan restart pada container yang terpasang padanya.

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 mengarah ke commit terakhir dari branch master, yang merupakan versi pengembangan terkini, jadi jangan gunakan :v3 pada mesin yang tidak ingin Anda debug pada hari Selasa.

WEBUI_PORT=8080 harus sesuai dengan port yang dipublikasikan, karena qBittorrent melakukan binding di dalam namespace gluetun dan aturan publikasi mengirim trafik host ke port 8080 di sana. Mengubah satu angka tanpa mengubah angka lainnya akan menyebabkan port tidak merespons. 127.0.0.1:8080:8080 menjaga antarmuka web tetap berada pada alamat loopback host. Penggunaan 8080:8080 secara langsung akan memublikasikan pada setiap antarmuka dan menulis aturan firewall-nya sendiri, yang merupakan penyebab port Docker yang dipublikasikan langsung melewati ufw.

Jalankan service, lalu periksa dengan urutan berikut:

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

docker compose ps seharusnya menampilkan gluetun sebagai healthy dan qbittorrent sebagai running. Kemudian konfirmasikan alamat keluar dari dalam namespace, yang merupakan pemeriksaan penentu untuk segalanya:

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

Field ip dalam JSON tersebut seharusnya adalah alamat penyedia VPN Anda. Jika yang muncul adalah alamat server Anda sendiri, aplikasi tersebut tidak berada di dalam tunnel, dan tidak ada hal di bawah ini yang akan berperilaku sesuai deskripsi.

Simpan kunci di luar file compose

gluetun.env menyimpan kredensial, dan file ini tidak disertakan dalam 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 penyedia layanan Anda. Atur file tersebut ke mode 600. Pahami konsekuensinya: kunci memang tidak masuk ke dalam repositori Anda, namun docker inspect gluetun tetap akan menampilkan setiap variabel lingkungan kepada siapa pun yang dapat mengakses Docker socket. File lingkungan dan rahasia di Docker Compose membahas opsi yang lebih aman.

Cara container di luar tunnel berkomunikasi dengan container di dalamnya

Kedua arah dapat berfungsi, dan masing-masing menggunakan nama yang berbeda. Kedua container memerlukan jaringan Docker yang sama, yaitu jaringan milik gluetun, karena container yang terpasang tidak memiliki jaringan sendiri. Cara kerja jaringan Docker Compose membahas pengaturan default tersebut.

Dari luar ke dalam, gunakan nama gluetun dan port tempat aplikasi mendengarkan (listen). Container reverse proxy dapat menjangkau antarmuka web qBittorrent di gluetun:8080. Tidak diperlukan entri ports: untuk hal ini, karena trafik antar-container tetap berada di dalam jaringan Docker dan tidak pernah menyentuh port host.

Dari dalam ke luar, gunakan nama service container lainnya, contohnya postgres:5432. Gluetun telah dapat melakukan resolusi nama container lain dari dalam namespace-nya sejak v3.41, jadi gunakan versi tersebut atau yang lebih baru jika nama gagal di-resolve.

Firewall gluetun menentukan siapa yang boleh membuka koneksi ke dalamnya. Trafik dari jaringan Docker milik gluetun sendiri diizinkan. Klien pada subnet yang berbeda, seperti laptop di LAN Anda atau container pada jaringan bridge terpisah, akan ditolak (dropped) sampai Anda menentukan subnet tersebut:

FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24

Makna yang didokumentasikan sudah tepat: subnet yang dipisahkan dengan koma yang diizinkan untuk diakses oleh Gluetun dan container yang berbagi stack jaringannya.

Koneksi masuk dari internet adalah masalah yang berbeda. Peer klien torrent datang dari sisi VPN, sehingga mempublikasikan port 6881 pada host tidak akan berpengaruh bagi mereka. Anda memerlukan port yang diteruskan (forwarded) dari penyedia VPN Anda, dan port tersebut harus dicantumkan di FIREWALL_VPN_INPUT_PORTS, yang mengizinkan port dari sisi server VPN. Ini adalah bagian yang sering kali tidak dikonfigurasi dengan benar pada media stack yang dibangun dengan Docker Compose.

Kill switch: apa yang terjadi saat tunnel terputus

Pola ini memiliki kompleksitas yang sepadan saat terjadi kegagalan. Container yang terhubung tidak memiliki rute kedua. Satu-satunya jalur keluar dari mesin tersebut adalah namespace yang digunakannya, sehingga saat tunnel mati, tidak ada jalur cadangan. Firewall Gluetun menerapkan aturan yang sama dari sisi lain: trafik keluar harus melalui tunnel atau menuju endpoint server VPN, dan semua trafik lainnya akan di-drop. Tidak ada celah di mana paket dapat bocor keluar melalui interface biasa saat klien melakukan koneksi ulang.

Gluetun memantau koneksinya sendiri. Setiap menit, ia mengirimkan ICMP echo (ping) ke alamat di HEALTH_ICMP_TARGET_IPS, yang secara default adalah 1.1.1.1,8.8.8.8. Setiap lima menit, ia melakukan dial TCP dan TLS (transport layer security) penuh ke HEALTH_TARGET_ADDRESSES, dengan default cloudflare.com:443,github.com:443. Ketika proses tersebut gagal, ia akan me-restart VPN di dalam container dan mencatatnya:

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 terhubung dengan mempertimbangkan urutan tersebut. Baris seperti connection refused, operation not permitted, dan i/o timeout di dalam aplikasi adalah konsekuensi dari tunnel yang mati, bukan penyebabnya. Dokumentasi Gluetun menyatakan hal ini secara langsung, karena banyak pengguna melaporkan konsekuensinya dan menghabiskan waktu berjam-jam untuk menelusurinya.

HEALTH_RESTART_VPN=on adalah pengaturan default dan harus tetap aktif. Matikan hanya saat Anda sedang melakukan debugging terhadap kegagalan spesifik, karena jika dimatikan, tunnel yang mati akan tetap mati.

Pengurutan: menghentikan stack agar tidak berjalan sebelum tunnel aktif

Image ini menyertakan Docker healthcheck:

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

Perintah tersebut menjalankan salinan gluetun kedua yang berumur pendek, yang melakukan kueri ke server kesehatan dari instance yang sedang berjalan di http://127.0.0.1:9999/. Tunnel yang berfungsi akan menjawab 200 OK. Tunnel yang rusak akan menjawab 500 Internal server error dengan string error, dan container akan ditandai sebagai tidak sehat (unhealthy) setelah satu kali kegagalan.

condition: service_healthy adalah mekanisme yang menunggu kondisi tersebut. depends_on: [gluetun] biasa hanya menunggu container untuk start, yang terjadi beberapa detik sebelum handshake selesai, sehingga aplikasi berjalan pada jaringan yang mati dan sering kali menyerah pada upaya koneksi pertamanya. Healthcheck di Docker Compose membahas sintaksis dan kolom pengaturan waktu tersebut.

Satu batasan sering kali mengecoh pengguna. Compose mengevaluasi kondisi tersebut hanya sekali, saat container dibuat. Compose tidak akan menghentikan atau me-restart aplikasi di kemudian hari jika gluetun menjadi tidak sehat. Fitur auto-healing internal gluetun menangani kasus tersebut, itulah sebabnya gluetun me-restart proses VPN alih-alih container-nya.

Periksa kebocoran DNS sebelum Anda memercayai konfigurasi

DNS (domain name system) adalah kebocoran yang tetap ada meskipun tunnel sudah benar. Gluetun menjalankan resolver-nya 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 apa adanya agar pencarian Anda terenkripsi dan melewati tunnel.

Pengaturan yang merusak hal ini adalah DNS_UPSTREAM_PLAIN_ADDRESSES. Pengguna sering mengubahnya saat nama domain gagal di-resolve dan mereka ingin router atau resolver penyedia layanan mereka yang menjawab. Dokumentasi Gluetun menyatakan risikonya dengan jelas: semua trafik DNS tidak akan melewati tunnel VPN dan akan bocor keluar. Trafik Anda tetap privat. Daftar hostname Anda tidak. Versi WireGuard dari kesalahan yang sama dibahas di DNS yang berhenti melakukan resolusi melalui tunnel WireGuard.

Untuk mengujinya, atur HTTPPROXY=on pada gluetun dan publikasikan 8888:8888/tcp, lalu arahkan browser ke proxy tersebut dan muat situs uji kebocoran DNS. Hasilnya harus menunjukkan penyedia layanan Anda atau Cloudflare, bukan router rumah Anda. Dokumentasi Gluetun sendiri memperingatkan bahwa beberapa tes kebocoran melaporkan hasil yang aneh, karena resolver di dalam namespace adalah perantara caching lokal, bukan server yang menjawab kueri tersebut secara langsung. Anggap negara yang salah atau resolver ISP Anda sendiri sebagai indikasi nyata adanya kebocoran.

Menambahkan Tailscale di samping sidecar VPN, dan mana yang menang

Tailscale adalah jaringan overlay yang dibangun di atas WireGuard untuk menjangkau mesin Anda sendiri, dan orang-orang menjalankannya di samping VPN penyedia layanan untuk menjaga jalur admin ke dalam stack. Keduanya jarang berkonflik, karena alasan yang patut dipahami. Dokumentasi Tailscale menyatakan perilaku defaultnya: ia bertindak sebagai jaringan overlay, hanya merutekan trafik antar perangkat yang menjalankan Tailscale, dan tidak menyentuh trafik internet publik Anda.

Jadi jawabannya bergantung pada satu pengaturan.

  • Tailscale dalam containernya sendiri, konfigurasi default: ia tidak pernah melihat trafik keluar aplikasi. Gluetun membawa semuanya. Tailscale menjangkau aplikasi di gluetun:8080, persis seperti container luar lainnya.
  • Tailscale dilampirkan ke namespace gluetun dengan network_mode: "service:gluetun": ia memerlukan cap_add sendiri dari net_admin dan net_raw, karena kapabilitas tidak disertakan bersama namespace. Dalam mode jaringan userspace default, TS_USERSPACE aktif, tailscaled tidak membuat antarmuka sama sekali dan bekerja sebagai proxy SOCKS5 atau HTTP, sehingga tidak dapat mengubah perutean. Gluetun tetap membawa segalanya.
  • Hal yang sama, dengan TS_USERSPACE=false: tailscaled membuat perangkat tunnel dan memasang rute, tetapi hanya untuk rentang tailnet 100.64.0.0/10 ditambah rute subnet apa pun yang Anda iklankan dengan TS_ROUTES. Trafik publik tetap keluar melalui gluetun.
  • Salah satu dari hal di atas dengan exit node yang dipilih, sudo tailscale set --exit-node=<exit-node-ip>: Tailscale mengklaim rute default dan menang. Jangan gabungkan itu dengan gluetun. Satu rute default, satu pemilik.

Jika rute yang diiklankan tersebut adalah tujuannya, dan Anda ingin seluruh jaringan privat di belakang kotak tersebut dapat dijangkau alih-alih hanya kotaknya saja, menjalankan router subnet Tailscale di VPS mencakup persetujuan rute, penerusan IP, dan flag sisi klien yang TS_ROUTES sendiri tidak selesaikan.

Jika Tailscale ada di sana untuk memberi Anda URL admin alih-alih rute, tailscale serve dan tailscale funnel menempatkan HTTPS di depan gluetun:8080 untuk tailnet Anda, dengan hanya funnel yang membukanya ke internet publik.

Satu efek samping terlihat saat Tailscale berjalan di dalam tunnel. Peer-nya melihat alamat penyedia VPN, jadi perkirakan ia akan lebih sering kembali ke relay. tailscale status menunjukkan relay "..." di samping peer alih-alih direct saat hal itu terjadi. Koneksi tetap berfungsi dan lebih lambat. Jika overlay adalah satu-satunya hal yang sebenarnya Anda butuhkan, perbedaan antara WireGuard biasa dan Tailscale adalah titik awal yang lebih baik.

Apa yang rusak, 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 layanan tersebut. Pindahkan ke gluetun.

Compose menolak seluruh file. Sebuah layanan tidak dapat menetapkan network_mode dan networks secara bersamaan. Letakkan network pada gluetun.

Container lain tidak dapat melakukan resolusi terhadap aplikasi. curl: (6) Could not resolve host: qbittorrent adalah perilaku yang benar, karena container yang terpasang tidak bergabung dengan network apa pun dan tidak mendaftarkan nama. Gunakan gluetun dan port-nya.

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

Aplikasi tidak memiliki network setelah Anda menyentuh gluetun. Melakukan restart atau membuat ulang gluetun akan memutus konektivitas untuk semua yang terpasang padanya. Restart container-container tersebut.

Halaman kecil dimuat, tetapi halaman besar menggantung. Itu adalah masalah MTU (maximum transmission unit). Tunnel menambahkan overhead, dan ada sesuatu di jalur tersebut yang menjatuhkan paket berukuran besar tanpa mengirimkan error kembali. Turunkan WIREGUARD_MTU, coba 1400, lalu 1320.

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

FAQ

Mengapa port yang dipublikasikan kontainer saya berhenti berfungsi di balik Gluetun?

Karena network_mode: "service:gluetun" menempatkan kontainer di dalam network namespace milik Gluetun, dan sebuah namespace hanya memiliki satu alamat IP serta satu set port yang mendengarkan (listening). Aplikasi tetap berjalan, namun aturan publikasi harus berada pada kontainer yang memiliki namespace tersebut. Pindahkan daftar ports: ke service Gluetun. Jika Anda membiarkannya pada service yang terlampir, Docker bahkan tidak akan membuatnya: Error response from daemon: conflicting options: port publishing and the container type network mode.

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

Gunakan nama service Gluetun dan port yang didengarkan oleh aplikasi, contohnya gluetun:8080. Kontainer yang terlampir tidak memiliki jaringan Docker sendiri, sehingga namanya tidak akan pernah ter-resolve. Tidak ada yang perlu dipublikasikan untuk trafik antar-kontainer. Sebaliknya, kontainer di dalam namespace dapat mengakses kontainer luar melalui nama servicenya, seperti postgres:5432, pada Gluetun v3.41 ke atas. Klien pada subnet berbeda, seperti laptop di LAN Anda, akan diblokir oleh firewall Gluetun sampai Anda menambahkan subnet tersebut ke FIREWALL_OUTBOUND_SUBNETS.

Apakah Gluetun berfungsi sebagai kill switch saat koneksi VPN terputus?

Ya, karena dua alasan sekaligus. Kontainer yang terlampir tidak memiliki rute selain yang ada di dalam namespace bersama, sehingga tunnel yang mati membuatnya tidak memiliki jalur keluar dari mesin. Firewall Gluetun juga hanya mengizinkan trafik keluar melalui tunnel dan menuju endpoint server VPN. Gluetun kemudian me-restart VPN secara internal, mencatat WARN [vpn] restarting VPN because it failed to pass the healthcheck, alih-alih keluar (exit), karena setiap kontainer yang terlampir akan kehilangan jaringannya jika 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 antar-perangkat di tailnet Anda dan membiarkan trafik publik apa adanya. Dalam mode userspace default image kontainer, Tailscale tidak membuat interface sama sekali, sehingga tidak dapat memengaruhi routing. Dengan TS_USERSPACE=false, Tailscale hanya menginstal 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, dan dalam kondisi ini Tailscale yang menang. Pilih satu produk saja untuk mengelola rute default alih-alih menumpuk keduanya.