Gluetun: Akses Host dan Container Lain
Container di balik Gluetun tidak memiliki antarmuka sendiri. Publikasikan port melalui Gluetun dan buka hanya subnet yang perlu diakses di luar tunnel.
Apa yang terjadi saat container bergabung dengan jaringan gluetun
Container yang menetapkan network_mode: service:gluetun tidak memiliki antarmuka jaringan sendiri. Container tersebut bergabung dengan namespace jaringan gluetun. Karena itu, publikasi port dan aturan firewall tidak lagi menjadi properti container tersebut, melainkan menjadi properti service gluetun. Semua jawaban di bawah ini berasal dari fakta tersebut.
Namespace jaringan adalah salinan privat stack jaringan milik kernel. Namespace ini memiliki antarmuka, tabel routing, aturan firewall, dan socket listening sendiri. Secara default, Docker memberikan satu namespace kepada setiap container. Saat Anda menulis network_mode: service:gluetun, Docker melewati langkah tersebut dan menempatkan container baru di dalam namespace yang sudah dimiliki gluetun. Container tetap memiliki filesystem dan file /etc/hosts sendiri. File kedua tersebut akan penting nanti.
Anda dapat melihatnya secara langsung.
docker inspect -f '{{.HostConfig.NetworkMode}}' qbittorrentPerintah tersebut mencetak container:, diikuti ID container gluetun. Pada container biasa, perintah tersebut akan mencetak bridge. Panduan ini melanjutkan routing trafik Docker melalui VPN dengan gluetun: tunnel sudah berfungsi, tetapi sekarang tidak ada yang dapat berkomunikasi dengan container tersebut.
Publish port pada gluetun, bukan pada aplikasi
Hapus blok ports: pada service yang menetapkan network_mode. Jika tidak, Docker menolak membuat container:
Error response from daemon: conflicting options: port publishing and the container type network modeAlasannya jelas. Mempublikasikan port berarti menambahkan aturan NAT (network address translation) yang meneruskan port host ke namespace jaringan milik container. Container ini tidak memiliki namespace jaringan sendiri. Pindahkan pemetaan tersebut ke service gluetun. Nomor port tidak berubah karena aplikasi tetap mendengarkan pada port tersebut di dalam namespace bersama.
services:
gluetun:
ports:
- "8080:8080/tcp" # qBittorrent web UI
qbittorrent:
network_mode: "service:gluetun"
# no ports: block hereBlok expose: pada service dependen juga tidak berguna. Blok networks: di sana akan menghentikan proses sepenuhnya. Compose melaporkan bahwa service tersebut mendeklarasikan network_mode dan networks yang saling eksklusif, lalu menolak memuat file tersebut.
Dampak lainnya baru terlihat kemudian. Semua container dalam namespace tersebut berbagi satu ruang port. Karena itu, dua aplikasi yang sama-sama menggunakan 8080 secara default akan mengalami konflik. Aplikasi yang dimulai kedua gagal dengan error address already in use. Ubah salah satunya dalam konfigurasinya sendiri, misalnya variabel WEBUI_PORT pada image LinuxServer qBittorrent, lalu publikasikan nomor baru tersebut pada gluetun.
Bagaimana container di belakang gluetun saling terhubung?
Di dalam namespace tersebut, keduanya sudah berbagi interface loopback. Container di belakang gluetun dapat mengakses container saudaranya melalui 127.0.0.1:<port> tanpa melibatkan jaringan Docker.
Dari luar namespace, container tersebut tidak memiliki nama. DNS tertanam Docker memetakan nama service ke alamat service tersebut pada jaringan yang ditentukan pengguna, sedangkan container ini tidak memiliki alamat pada jaringan mana pun. Karena itu, container biasa seperti Sonarr tidak mengakses klien torrent melalui http://qbittorrent:8080. Sonarr mengaksesnya melalui http://gluetun:8080 karena socket tersebut listening di namespace gluetun, pada alamat milik gluetun. Hal ini mengejutkan pengguna yang memahami cara kerja jaringan Docker Compose dan nama service dan mengira penamaan standar tetap berlaku. Cara ini juga berfungsi tanpa memublikasikan apa pun ke host karena kedua container berada pada jaringan Compose yang sama.
Periksa DNS sebelum melakukan debug lainnya. Gluetun menjalankan resolver sendiri dan menulis ulang /etc/resolv.conf di dalam container-nya sendiri, tetapi /etc/resolv.conf adalah file per container. Jadi, file yang ditulis gluetun bukan file yang dibaca aplikasi Anda.
docker exec qbittorrent cat /etc/resolv.confBagaimana cara mengakses service yang berjalan pada Docker host?
Gunakan host.docker.internal. Pengaturan ini memerlukan dua konfigurasi di dua tempat berbeda karena ada dua hal yang bermasalah.
Nama harus ditambahkan terlebih dahulu. /etc/hosts berlaku per container, jadi entri extra_hosts harus ditempatkan pada container aplikasi, bukan pada gluetun.
prowlarr:
network_mode: "service:gluetun"
extra_hosts:
- "host.docker.internal:host-gateway"host-gateway adalah nilai khusus yang diganti Docker dengan alamat internal host itu sendiri. Pada instalasi Docker Linux biasa, alamat tersebut adalah alamat bridge docker0, yang umumnya 172.17.0.1. Konfirmasikan alamat Anda dengan ip -4 addr show docker0 pada VPS. Docker Desktop menyelesaikan nama ini secara otomatis. Karena itu, panduan yang ditulis untuk laptop sering tidak menyertakan baris extra_hosts, dan file yang sama kemudian gagal pada server.
Rute harus ditambahkan setelah itu. Menambahkan nama hanya memberi tahu container alamat yang harus digunakan. Paket tetap keluar melalui rute default gluetun, yaitu tunnel, lalu firewall gluetun menjatuhkannya. Gejalanya adalah koneksi yang menunggu lalu mengalami timeout, bukan koneksi yang ditolak. Penolakan berarti paket tiba dan ada sesuatu yang menjawab tidak. Timeout berarti paket tidak pernah tiba.
gluetun:
environment:
- FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32Selanjutnya, pastikan service pada host benar-benar listening pada alamat tersebut. Server PostgreSQL yang hanya terikat ke 127.0.0.1 tidak dapat diakses dari container mana pun, baik melalui tunnel maupun tidak, karena 127.0.0.1 di dalam namespace adalah loopback milik namespace itu sendiri. Ikat service ke 172.17.0.1 sebagai gantinya. Dengan demikian, service menerima koneksi dari container tanpa terbuka pada interface publik. Verifikasikan dengan ss -lntp | grep 5432 pada host.
Apa yang sebenarnya diubah oleh FIREWALL_OUTBOUND_SUBNETS
Dokumentasi gluetun menjelaskannya sebagai daftar subnet yang dipisahkan koma dan boleh diakses oleh gluetun serta container yang berbagi network stack dengannya. Dokumentasi tersebut juga menyebutkan bahwa pengaturan ini melibatkan perubahan firewall dan routing. Kedua hal ini penting. Gluetun menambahkan route untuk setiap subnet yang tercantum melalui gateway Docker bridge, sehingga paket menuju alamat tersebut keluar melalui eth0, bukan melalui tunnel. Gluetun juga membuka firewall untuk subnet tersebut karena secara default gluetun menjatuhkan trafik outbound yang tidak menuju server VPN.
Tulis nilainya tanpa spasi setelah koma.
- FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,192.168.1.0/24,100.64.7.9/32Ada dua properti yang mudah terlewat. Pengaturan ini berlaku pada level namespace, sehingga diterapkan ke setiap container di belakang gluetun, bukan hanya container yang Anda maksud. Pengaturan ini juga hanya berlaku untuk trafik outbound: pengaturan ini mengatur koneksi yang dimulai oleh container. Koneksi yang masuk melalui port yang dipublikasikan menggunakan jalur berbeda dan tidak memerlukan entri di sini.
Mengakses UI web dari peer Tailscale
Tailscale memberikan setiap mesin alamat dalam 100.64.0.0/10, yaitu rentang yang dicadangkan untuk carrier-grade NAT. Kedua arah tersebut memerlukan penanganan yang berbeda.
Arah inbound lebih sederhana. Mempublikasikan 8080:8080 pada gluetun akan mengikat port tersebut ke semua alamat host. Antarmuka tailscale0 milik host termasuk salah satunya. Karena itu, peer dapat membuka http://<machine-name>:8080 dan mengakses container. Gluetun tidak terlibat dalam jalur tersebut, karena aturan NAT Docker berada pada host, di luar namespace.
Agar UI hanya dapat diakses melalui tailnet, ikat port yang dipublikasikan ke alamat Tailscale milik host, bukan ke semua alamat.
ports:
- "100.101.102.103:8080:8080/tcp"Cari alamat tersebut dengan tailscale ip -4 pada host. Dalam kasus ini, binding memberikan kontrol yang lebih kuat daripada aturan firewall, karena port sama sekali tidak dibuka pada antarmuka publik. Cara ini juga menghindari masalah yang dijelaskan dalam Docker memublikasikan port langsung melewati ufw.
Arah outbound adalah saat FIREWALL_OUTBOUND_SUBNETS kembali berperan. Jika container harus menghubungi peer, tambahkan alamat peer tersebut. Sebaiknya gunakan /32 per peer, bukan seluruh /10. Nama MagicDNS tidak akan di-resolve di dalam container karena container tidak menggunakan resolver milik host. Karena itu, gunakan alamat numerik 100.x atau tetapkan alamat tersebut dengan baris extra_hosts. Hal yang sama berlaku saat Anda menjalankan server kontrol Tailscale sendiri dengan Headscale.
File compose lengkap untuk pola umum
Satu download client di balik VPN, dua UI web yang hanya merespons pada tailnet, dan satu container yang membaca database PostgreSQL yang berjalan pada 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-stoppedBaca file ini untuk memahami polanya, bukan nama produknya. Kedua UI dipublikasikan melalui gluetun dan di-bind ke alamat tailnet milik host, sehingga hanya merespons melalui Tailscale. Hanya Prowlarr yang memiliki baris extra_hosts karena Prowlarr adalah container yang melakukan resolusi host.docker.internal. FIREWALL_OUTBOUND_SUBNETS menetapkan dua alamat tunggal: alamat bridge Docker milik host agar Prowlarr dapat membuka koneksi database, serta satu peer tailnet.
Server PostgreSQL sengaja tidak dicantumkan dalam file ini. Server tersebut berjalan pada VPS sebagai system service biasa dan mendengarkan pada 172.17.0.1:5432. Lapisan ini sama seperti arr stack pada Docker Compose, tetapi databasenya dipindahkan ke luar Docker.
Simpan private key WireGuard di luar file compose. ${WIREGUARD_PRIVATE_KEY} membaca key tersebut dari file .env di sebelah file ini. Pola ini dibahas dalam env file dan secret untuk Docker Compose. Klausa condition: service_healthy menggunakan healthcheck yang sudah disediakan oleh image gluetun, sehingga tidak ada container yang dijalankan sebelum tunnel melaporkan bahwa dirinya aktif. Healthcheck Compose menjelaskan bentuk umumnya.
Memublikasikan pada setiap alamat, bukan hanya tailnet
Hapus prefiks alamat dan binding port pada 0.0.0.0, yang mencakup IP publik VPS. Lakukan ini hanya di belakang firewall yang Anda kendalikan, dan baca catatan ufw di atas terlebih dahulu.
ports:
- "8080:8080/tcp"Pastikan tunnel masih membawa trafik
Jalankan permintaan yang sama dua kali: sekali dari dalam namespace dan sekali dari host, lalu bandingkan hasilnya.
docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org
curl -s https://api.ipify.orgHasil pertama seharusnya menampilkan alamat keluar dari penyedia VPN Anda. Hasil kedua seharusnya menampilkan alamat VPS. Jika keduanya sama, trafik container tidak melewati tunnel. Semua perbaikan dalam panduan ini tidak akan berarti sampai masalah tersebut diperbaiki.
Tabel routing menunjukkan trafik mana yang melewati tunnel dan mana yang tidak.
docker run --rm --network=container:gluetun alpine:3.22 ip route showRute default harus mengarah ke interface tunnel, tun0. Di bawahnya, Anda akan melihat satu rute untuk setiap entri dalam FIREWALL_OUTBOUND_SUBNETS, yang mengarah ke gateway bridge Docker. Rute lain yang keluar melalui eth0 adalah trafik yang melewati VPN.
Control server Gluetun melaporkan IP publik yang sama pada port 8000, di /v1/publicip/ip. Versi terbaru mengharuskan Anda mengonfigurasi autentikasi untuk route control server. Konfigurasikan autentikasi tersebut sebelum mengandalkannya.
Kebocoran akibat satu subnet yang salah
FIREWALL_OUTBOUND_SUBNETS adalah celah yang sengaja dibuat pada firewall. Karena itu, ukuran celah menentukan ukuran risikonya. Berikut empat cara membuatnya terlalu besar:
0.0.0.0/0mengirim semua trafik ke luar tunnel. Dua pemeriksaan IP di atas mendeteksi hal ini pada proses pertama, karena keduanya akan mengembalikan alamat yang sama.- Rentang yang lebih luas daripada target. Membuka
10.0.0.0/8untuk menjangkau satu mesin di10.0.1.7juga membuka setiap alamat yang mungkin diiklankan oleh peer torrent dalam rentang tersebut. Tulis10.0.1.7/32. - Rentang yang tumpang tindih dengan alamat milik tunnel. Dokumentasi gluetun memperingatkan bahwa kondisi ini membuat gluetun mengirim trafik VPN melalui bridge, bukan melalui tunnel. Akibatnya, port forwarding tidak berfungsi. Periksa nilai
WIREGUARD_ADDRESSESsebelum membuka rentang privat apa pun. 100.64.0.0/10untuk Tailscale. Tindakan ini membuka sekitar empat juta alamat agar satu peer dapat dijangkau. Cantumkan peer yang diperlukan sebagai entri/32.
Ingat bahwa pengaturan ini berlaku untuk seluruh namespace. Membuka subnet agar indexer dapat menjangkau service pada host juga membuka subnet yang sama untuk torrent client yang menggunakan namespace tersebut. Jalankan kembali pemeriksaan IP publik setiap kali mengubah variabel ini, karena hanya pemeriksaan tersebut yang menunjukkan apakah perubahan berjalan sesuai tujuan Anda.
Apa yang rusak saat Anda me-restart gluetun
gluetun memiliki namespace tersebut, sehingga siklus hidup gluetun adalah siklus hidup namespace. Menjalankan container dependen saat gluetun berhenti akan langsung gagal:
Error response from daemon: cannot join network of a non running containerMe-restart gluetun di tempat merupakan kegagalan yang lebih sulit dideteksi. Container dependen tetap berjalan, sementara namespace tempat container tersebut terhubung dibangun ulang di bawahnya. Akibatnya, docker ps melaporkan bahwa semuanya sehat, tetapi tidak ada layanan yang merespons. Setelah mengubah service gluetun, buat ulang seluruh grup, bukan hanya me-restart salah satu bagiannya.
docker compose up -d --force-recreateHal yang sama berlaku untuk pembaruan image. Jika Anda melakukan pull image gluetun baru dan hanya membuat ulang service tersebut, container lain tetap menunjuk ke namespace yang sudah tidak ada.
FAQ
Mengapa Docker mengatakan "port publishing dan mode jaringan jenis container"?
Karena blok ports: masih terdapat pada service yang juga menetapkan network_mode: service:gluetun. Publishing port menambahkan aturan NAT yang meneruskan port pada host ke namespace jaringan milik container. Container dalam mode ini tidak memiliki namespace jaringan sendiri. Hapus blok ports: dari service tersebut, lalu tambahkan pemetaan yang sama ke service gluetun. Nomor port tetap sama karena aplikasi masih mendengarkan pada port tersebut di dalam namespace bersama.
Bagaimana container lain mengakses service yang berada di belakang gluetun?
Container dalam namespace yang sama saling mengakses melalui 127.0.0.1. Container di luarnya menggunakan nama service gluetun, sehingga http://gluetun:8080 berfungsi, sedangkan http://qbittorrent:8080 tidak. Container aplikasi tidak memiliki alamat pada jaringan Docker mana pun. Karena itu, server DNS internal tidak memiliki alamat yang dapat di-resolve untuk namanya. Publishing port tidak diperlukan untuk hal ini selama kedua container berbagi jaringan Compose.
Apa yang harus saya masukkan ke FIREWALL_OUTBOUND_SUBNETS?
Masukkan hanya alamat yang harus dituju container di belakang gluetun saat memulai koneksi. Tulis alamat tersebut sesempit mungkin. Satu mesin ditulis sebagai /32. Dua entri yang umum adalah host Docker di 172.17.0.1/32 dan satu /32 untuk setiap peer Tailscale yang Anda hubungi. Jangan pernah menambahkan 0.0.0.0/0. Jangan tambahkan rentang yang tumpang tindih dengan alamat tunnel VPN Anda sendiri. Koneksi masuk ke port yang dipublikasikan tidak memerlukan entri di sini.
Mengapa container tidak dapat me-resolve nama MagicDNS Tailscale saya?
MagicDNS bekerja dengan mengarahkan resolver host ke server DNS Tailscale. Container tidak menggunakan resolver host. Container menggunakan nilai yang ditetapkan oleh /etc/resolv.conf miliknya sendiri. Di belakang gluetun, nilai tersebut berasal dari konfigurasi DNS gluetun. Konfirmasikan dengan docker exec <container> cat /etc/resolv.conf. Gunakan alamat numerik 100.x milik peer, atau tetapkan nama tersebut secara manual melalui entri extra_hosts pada container itu.
Bagaimana cara memastikan trafik masih melewati VPN?
Jalankan satu request dari dalam namespace dan request yang sama dari host, lalu bandingkan hasilnya. docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org seharusnya mengembalikan alamat keluar dari penyedia VPN Anda, sedangkan curl -s https://api.ipify.org pada VPS mengembalikan alamat VPS. Hasil yang sama menunjukkan bahwa tunnel tidak membawa trafik container. Jalankan pemeriksaan ini lagi setiap kali mengubah FIREWALL_OUTBOUND_SUBNETS.