Docker container VPN uzerinden nasil yonlendirilir?
Gluetun ile VPN arkasina alinan containerlarda portlarin neden kayboldugunu ogrenin. Paylasilan ag ad alani mantigini ve calisan Docker Compose yapilandirmasini inceleyin.
Docker container'larını bir VPN üzerinden yönlendirdiğinizde portların kaybolma nedeni
Docker container'larını bir VPN üzerinden yönlendirmek için bir container'a tünel bağlantısını verir, ardından diğerlerini network_mode: "service:gluetun" ile onun ağ ad alanına (network namespace) eklersiniz. Bu ekleme işlemi, kullanıcıları şaşırtan kısımdır. Eklenen container artık kendi ağına sahip değildir; bu nedenle yayınlanan (published) portları ve Docker servis adı da onunla birlikte ortadan kalkar. Portları VPN container'ı üzerinde yayınlayın; diğer container'lar uygulamaya VPN container'ının adı üzerinden erişecektir.
Eklenen container üzerinde bir ports: bloğu bırakırsanız, Docker container'ı oluşturmayı tamamen reddeder:
Error response from daemon: conflicting options: port publishing and the container type network modeBurada kullanılan araç, WireGuard veya OpenVPN üzerinden ticari bir VPN (sanal özel ağ) sağlayıcısına bağlanan ve kendi güvenlik duvarını barındıran bir container olan Gluetun'dır. Ağustos 2026 itibarıyla güncel sürüm v3.41.3'tür. Örneklerde WireGuard ile Mullvad kullanılmıştır; bu nedenle sağlayıcınızdan alınmış bir hesaba ve anahtara ihtiyacınız vardır. Tüneli kendi donanımınızda sonlandırmayı tercih ederseniz, kendi WireGuard sunucunuzu bir VPS üzerinde çalıştırmak karşı ucu oluşturur ve Docker içinde wg-easy bunu bir web arayüzü ile sarmalar.
network_mode: "service:gluetun" gerçekte ne yapar
Her Docker container'ı normalde kendi ağ ad alanına (network namespace) sahiptir: kendi arayüzleri, yönlendirme tablosu, güvenlik duvarı kuralları ve dinleme soketleri. service: modu bu adımı atlar ve container'ı gluetun'un ad alanı içinde başlatır. Tek bir ad alanı, tek bir IP adresi anlamına gelir ve bu durum altı şeyi değiştirir.
- Uygulamanın kendine ait bir adresi yoktur. Adresi, gluetun'un adresidir.
- Uygulama hiçbir Docker ağına bağlı değildir, bu nedenle servis adı asla kaydedilmez ve çözümlenmez. Diğer container'lar
gluetunkullanmalıdır. - Ad alanı içindeki container'lar birbirine
localhostüzerinden erişir. - Aynı ad alanındaki iki container aynı portu dinleyemez. Gluetun belgeleri bu konuda nettir: bunun bir geçici çözümü yoktur.
- Yetenekler (capabilities) ad alanına değil, container'a aittir. Tünel arayüzünü oluşturduğu için
NET_ADMINve/dev/net/tunyetkileri gluetun'dadır. Bağlı olan container bunları devralmaz. - Compose, bir servisin hem
network_modehem denetworksayarını yaptığı dosyaları reddeder. gluetun'u ağlarınıza bağlayın, uygulama da onunla birlikte taşınacaktır.
gluetun'u yeniden başlatmak, ona bağlı olan her şeyin bağlantısını keser. Bu belgelenmiş bir davranıştır ve bağlantı koptuğunda gluetun'un çıkış yapmak yerine container içindeki VPN sürecini yeniden başlatmasının nedeni budur. gluetun'u kendiniz yeniden başlattığınızda veya yeniden oluşturduğunuzda, ona bağlı olan container'ları da yeniden başlatın.
Çalışan compose dosyası
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:v3 etiketi, v3 serisindeki en güncel kararlı sürümdür. :latest etiketi, geliştirme aşamasındaki master dalının son commit'ini işaret eder; bu nedenle, Salı günü hata ayıklamak istemediğiniz bir makinede :v3 sürümünü sabitleyin.
WEBUI_PORT=8080, yayınlanan port ile eşleşmelidir; çünkü qBittorrent, gluetun'un namespace'i içinde dinleme yapar ve yayınlama kuralı, ana makine trafiğini oradaki 8080 numaralı porta iletir. Birini değiştirip diğerini değiştirmezseniz port hiçbir yanıt vermez. 127.0.0.1:8080:8080, web arayüzünü ana makinenin loopback adresinde tutar. Yalın bir 8080:8080, tüm arayüzlerde yayın yapar ve kendi güvenlik duvarı kuralını yazar; Docker yayınlanan portlarının ufw kurallarını nasıl baypas ettiği bu şekilde gerçekleşir.
Servisi ayağa kaldırın ve ardından şu sırayla kontrol edin:
docker compose up -d
docker compose ps
docker compose logs gluetun | tail -30docker compose ps, gluetun'u healthy ve qbittorrent'i running olarak göstermelidir. Ardından, diğer her şeyin doğruluğunu belirleyen test olan, namespace içinden çıkış adresini onaylayın:
docker run --rm --network=container:gluetun alpine:3.22 sh -c "apk add wget && wget -qO- https://ipinfo.io"Bu JSON içindeki ip alanı, VPN sağlayıcınızın adresi olmalıdır. Eğer bu adres sunucunuzun kendi adresi ise, uygulama tünel içinde değildir ve aşağıda belirtilen hiçbir şey tarif edildiği gibi çalışmayacaktır.
Anahtarları compose dosyasının dışında tutun
gluetun.env kimlik bilgilerini barındırır ve git deposuna dahil edilmemelidir:
WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
WIREGUARD_ADDRESSES=10.64.222.21/32Her iki değer de sağlayıcınızın hesap panelinden oluşturduğunuz bir WireGuard yapılandırma dosyasından gelir. Dosya izinlerini 600 moduna ayarlayın. Bu yöntemin sağladığı avantaj konusunda gerçekçi olun: anahtar deponuzun dışında kalır, ancak docker inspect gluetun Docker soketine erişimi olan herkese tüm ortam değişkenlerini açıkça gösterir. Docker Compose içinde ortam dosyaları ve gizli veriler bölümü daha güvenli seçenekleri ele almaktadır.
Tünel dışındaki bir container'ın tünel içindekilerle nasıl iletişim kurduğu
Her iki yön de çalışır ve her biri farklı bir isim kullanır. İki container'ın paylaşılan bir Docker ağına ihtiyacı vardır; bu da bağlı olan container'ın kendi ağı olmadığından gluetun'un ağı anlamına gelir. Docker Compose ağlarının nasıl yapılandırıldığı varsayılanları kapsar.
Dışarıdan içeriye doğru, gluetun'un adını ve uygulamanın dinlediği portu kullanın. Bir reverse proxy container'ı, qBittorrent web arayüzüne gluetun:8080 üzerinden erişir. Bunun için herhangi bir ports: girdisine gerek yoktur, çünkü container'dan container'a trafik Docker ağı üzerinde kalır ve hiçbir zaman host portuna temas etmez.
İçeriden dışarıya doğru, diğer container'ın servis adını kullanın; örneğin postgres:5432. Gluetun, v3.41 sürümünden beri kendi namespace'i içinden diğer container adlarını çözümleyebilmektedir; bu nedenle bir isim çözümlenemiyorsa o sürümü veya daha yenisini sabitleyin.
Gluetun'un güvenlik duvarı, kendisine kimin bağlantı açabileceğine karar verir. Gluetun'un kendi Docker ağından gelen trafiğe izin verilir. Farklı bir alt ağdaki bir istemci, yerel ağınızdaki (LAN) bir dizüstü bilgisayar veya ayrı bir bridge ağındaki bir container, siz o alt ağı tanımlayana kadar engellenir:
FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24Belgelenen anlam kesindir: Gluetun'un ve onun ağ yığınını paylaşan container'ların erişmesine izin verilen, virgülle ayrılmış alt ağlar.
İnternetten gelen gelen bağlantılar ayrı bir sorundur. Bir torrent istemcisinin eşleri (peers) VPN tarafından gelir, bu nedenle 6881 numaralı portu host üzerinde yayınlamak onlar için bir işe yaramaz. Sağlayıcınızdan yönlendirilmiş bir porta ve bu portun FIREWALL_VPN_INPUT_PORTS içinde listelenmesine ihtiyacınız vardır; bu, VPN sunucusu tarafından gelen portlara izin verir. Bu, Docker Compose ile oluşturulan medya yığınlarının çoğunda eksik bırakılan kısımdır.
Kill switch: Tünel bağlantısı koptuğunda ne olur
Bu model, hata durumundaki karmaşıklığı sayesinde değer kazanır. Bağlı container'ın ikinci bir rotası yoktur. Makine dışına çıkan tek yolu paylaştığı namespace'tir; bu nedenle tünel kapandığında geri dönülebilecek başka bir yol kalmaz. Gluetun'ın güvenlik duvarı, aynı kuralı diğer taraftan zorunlu kılar: giden trafik tünel üzerinden veya VPN sunucusu uç noktasına gider, diğer her şey reddedilir. İstemci yeniden bağlanırken paketlerin düz arayüzden sızabileceği bir zaman aralığı oluşmaz.
Gluetun kendi bağlantısını izler. Her dakika HEALTH_ICMP_TARGET_IPS içindeki adreslere (varsayılan olarak 1.1.1.1,8.8.8.8) bir ICMP echo (ping) gönderir. Her beş dakikada bir, varsayılan olarak cloudflare.com:443,github.com:443 olan HEALTH_TARGET_ADDRESSES adresine tam bir TCP ve TLS (transport layer security) bağlantısı gerçekleştirir. Bunlar başarısız olduğunda, container içindeki VPN'i yeniden başlatır ve bunu günlüğe kaydeder:
WARN [vpn] restarting VPN because it failed to pass the healthcheck: periodic check: dialing: dial tcp4: lookup cloudflare.com: i/o timeoutBağlı container'ın günlüklerini bu düzeni göz önünde bulundurarak okuyun. Uygulama içindeki connection refused, operation not permitted ve i/o timeout gibi satırlar, tünelin kopmasının bir sonucudur, nedeni değildir. Gluetun belgeleri bunu açıkça belirtir; çünkü kullanıcılar sonucu rapor edip saatlerce bunun peşinden koşmaktadır.
HEALTH_RESTART_VPN=on varsayılan ayardır ve açık kalmalıdır. Yalnızca belirli bir hatayı ayıklarken kapatın; çünkü bu ayar kapalıyken kopan bir tünel, kendiliğinden tekrar ayağa kalkmaz.
Sıralama: Yığının tünel hazır olmadan başlamasını engelleme
İmaj, bir Docker healthcheck içerir:
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /gluetun-entrypoint healthcheckBu komut, çalışan gluetun örneğinin sağlık sunucusunu http://127.0.0.1:9999/ adresinden sorgulayan, kısa ömürlü ikinci bir gluetun kopyası çalıştırır. Çalışan bir tünel 200 OK yanıtını verir. Bağlantısı kopuk bir tünel ise hata metniyle birlikte 500 Internal server error yanıtını döndürür ve container tek bir başarısızlığın ardından sağlıksız (unhealthy) olarak işaretlenir.
condition: service_healthy, bu durumu bekleyen yapıdır. Basit depends_on: [gluetun] yalnızca container'ın başlamasını bekler; bu durum el sıkışma tamamlanmadan birkaç saniye önce gerçekleştiği için uygulama ölü bir ağ üzerinde başlar ve genellikle ilk bağlantı denemesinde pes eder. Docker Compose içinde sağlık kontrolleri bölümü, sözdizimini ve zamanlama alanlarını detaylandırır.
Bir sınırlama kullanıcıları yanıltabilir. Compose, bu koşulu yalnızca container'ı oluşturduğu anda bir kez değerlendirir. gluetun daha sonra sağlıksız duruma düşerse uygulamayı durdurmaz veya yeniden başlatmaz. gluetun'un kendi içindeki otomatik iyileştirme mekanizması bu durumu karşılar; bu nedenle VPN süreci container yerine kendi içinde yeniden başlatılır.
Kurulumu güvenli kabul etmeden önce DNS sızıntısı kontrolü yapın
DNS (domain name system), doğru yapılandırılmış bir tünelde bile sızıntıya neden olabilen bir bileşendir. Gluetun, kendi çözümleyicisini (resolver) isim alanı (namespace) içinde çalıştırır ve sorguları varsayılan olarak DoT (DNS over TLS) üzerinden Cloudflare'e iletir: DNS_UPSTREAM_RESOLVER_TYPE=dot ve DNS_UPSTREAM_RESOLVERS=cloudflare. Bu ayarları değiştirmeyin; böylece sorgularınız şifrelenir ve tünel üzerinden iletilir.
Bu durumu bozan ayar DNS_UPSTREAM_PLAIN_ADDRESSES değeridir. Kullanıcılar, bir alan adı çözümlenemediğinde ve yönlendiricilerinin veya servis sağlayıcılarının çözümleyicisini kullanmak istediklerinde bu ayara başvururlar. Gluetun belgeleri, bunun bedelini açıkça belirtir: tüm DNS trafiği VPN tüneli üzerinden geçmez ve tünel dışına sızar. Trafiğiniz gizli kalsa da, ziyaret ettiğiniz ana bilgisayar adlarının (hostname) listesi gizli kalmaz. Aynı hatanın WireGuard sürümü için WireGuard tüneli üzerinden çözümlenmeyen DNS bölümüne bakabilirsiniz.
Test etmek için Gluetun üzerinde HTTPPROXY=on ayarını yapın ve 8888:8888/tcp portunu dışarıya açın, ardından bir tarayıcıyı bu proxy'ye yönlendirerek bir DNS sızıntı testi çalıştırın. Sonuç, ev yönlendiricinizi değil, VPN sağlayıcınızı veya Cloudflare'i göstermelidir. Gluetun'un kendi belgeleri, bazı sızıntı testlerinin yanıltıcı sonuçlar verebileceği konusunda uyarır; çünkü isim alanı içindeki çözümleyici, nihai yanıtı veren sunucudan ziyade yerel bir önbellekleme aracıdır. Yanlış bir ülke bilgisi veya kendi ISS'nizin çözümleyicisinin görünmesi, sızıntının gerçek bir göstergesi olarak kabul edilmelidir.
VPN sidecar yanına Tailscale eklemek ve hangisinin öncelikli olduğu
Tailscale, kendi makinelerinize erişmek için WireGuard üzerine inşa edilmiş bir katman ağıdır (overlay network) ve kullanıcılar, yığına bir yönetici erişim yolu bırakmak için bunu bir sağlayıcı VPN'i ile yan yana çalıştırırlar. İkisi nadiren çakışır; bunun anlaşılması gereken bir nedeni vardır. Tailscale belgeleri varsayılan durumu şöyle belirtir: Bir katman ağı olarak hareket eder, yalnızca Tailscale çalıştıran cihazlar arasındaki trafiği yönlendirir ve genel internet trafiğinize dokunmaz.
Bu nedenle cevap, tek bir ayara bağlıdır.
- Kendi container'ı içindeki Tailscale, varsayılan yapılandırma: Uygulamanın giden trafiğini asla görmez. Gluetun trafiğin tamamını taşır. Tailscale, uygulamaya tıpkı diğer dış container'lar gibi
gluetun:8080üzerinden ulaşır. network_mode: "service:gluetun"ile gluetun namespace'ine eklenmiş Tailscale: Kendicap_add,net_adminvenet_rawdeğerlerine ihtiyaç duyar, çünkü yetenekler (capabilities) namespace ile birlikte gelmez. Varsayılan userspace ağ modundaTS_USERSPACEaçıkken, tailscaled hiçbir arayüz oluşturmaz ve SOCKS5 veya HTTP proxy olarak çalışır, bu nedenle yönlendirmeyi değiştiremez. Gluetun her şeyi taşımaya devam eder.- Aynı durum,
TS_USERSPACE=falseile: tailscaled bir tünel cihazı oluşturur ve rotalar ekler, ancak bu sadece100.64.0.0/10tailnet aralığı veTS_ROUTESile duyurduğunuz alt ağ rotaları içindir. Genel trafik gluetun üzerinden çıkmaya devam eder. - Yukarıdakilerden herhangi biri seçili bir çıkış düğümü (exit node) ile kullanıldığında,
sudo tailscale set --exit-node=<exit-node-ip>: Tailscale varsayılan rotayı sahiplenir ve öncelikli olur. Bunu gluetun ile birleştirmeyin. Tek bir varsayılan rota, tek bir sahip.
Eğer amaç bu duyurulan rotalarsa ve kutunun kendisi yerine kutunun arkasındaki tüm özel ağa ulaşılabilir olmasını istiyorsanız, bir VPS üzerinde Tailscale subnet router çalıştırmak, rota onayını, IP yönlendirmeyi ve TS_ROUTES bayrağının tek başına eksik bıraktığı istemci tarafı ayarlarını kapsar.
Eğer Tailscale bir rota yerine size bir yönetici URL'si sağlamak için oradaysa, tailscale serve ve tailscale funnel, tailnet'iniz için gluetun:8080 önüne HTTPS koyar ve sadece funnel özelliği bunu genel internete açar.
Bir yan etki, Tailscale tünel içinde çalıştığında görülür. Eşleri (peers) VPN sağlayıcısının adresini görür, bu yüzden daha sık rölelere (relays) düşmesini bekleyin. Bu gerçekleştiğinde tailscale status, bir eşin yanında direct yerine relay "..." gösterir. Bağlantı çalışır ancak daha yavaştır. Eğer ihtiyacınız olan tek şey katman ağı ise, saf WireGuard ile Tailscale arasındaki fark daha iyi bir başlangıç noktasıdır.
Neler bozulur ve göreceğiniz hata mesajları
Docker, uygulama container'ını oluşturmayı reddediyor. Error response from daemon: conflicting options: port publishing and the container type network mode hatası, bağlı serviste hala bir ports: bloğunun bulunduğunu gösterir. Bu bloğu gluetun'a taşıyın.
Compose tüm dosyayı reddediyor. Bir servis aynı anda hem network_mode hem de networks ayarını yapamaz. Ağ yapılandırmasını gluetun üzerinde tanımlayın.
Başka bir container uygulamayı çözümleyemiyor. curl: (6) Could not resolve host: qbittorrent beklenen davranıştır; çünkü bağlı container hiçbir ağa katılmamış ve bir isim kaydetmemiştir. gluetun ve ilgili portu kullanın.
İkinci bağlı container başlamıyor. Aynı namespace içindeki iki süreç aynı portu dinleyemez; kaybeden süreç adresin kullanımda olduğuna dair hata verir. Uygulamanın dahili portunu değiştirin veya ikinci bir gluetun çalıştırın.
gluetun'a müdahale ettikten sonra uygulamanın ağ bağlantısı kesildi. gluetun'ı yeniden başlatmak veya yeniden oluşturmak, ona bağlı olan her şeyin bağlantısını koparır. Bu container'ları yeniden başlatın.
Küçük sayfalar yükleniyor ancak büyük sayfalar takılıyor. Bu durum MTU (maximum transmission unit) ile ilgilidir. Tünel ek yük getirir ve yol üzerindeki bir cihaz, hata mesajı göndermeden büyük paketleri düşürür. WIREGUARD_MTU değerini düşürün, sırasıyla 1400 ve 1320 değerlerini deneyin.
gluetun hiçbir zaman sağlıklı (healthy) duruma geçmiyor. Başlangıç kontrolü ilk şüphelileri belirtir: WARN [vpn] restarting VPN because it failed to pass the healthcheck: startup check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout. Anahtarın süresinin dolup dolmadığını, sunucu listesinin güncelliğini ve ana makine güvenlik duvarınızın giden UDP trafiğini engelleyip engellemediğini kontrol edin.
FAQ
Why did my container's published ports stop working behind Gluetun?
Because network_mode: "service:gluetun" puts the container inside gluetun's network namespace, and a namespace has one IP address and one set of listening ports. The app keeps listening, but the publish rule has to live on the container that owns the namespace. Move the ports: list to the gluetun service. If you left it on the attached service, Docker will not even create it: Error response from daemon: conflicting options: port publishing and the container type network mode.
How do I reach a container inside the VPN tunnel from a container outside it?
Use gluetun's service name and the port the app listens on, for example gluetun:8080. The attached container is on no Docker network of its own, so its own name never resolves. Nothing needs publishing for container to container traffic. Going the other way, a container inside the namespace reaches an outside container by its service name, such as postgres:5432, on Gluetun v3.41 and newer. A client on a different subnet, such as a laptop on your LAN, is dropped by gluetun's firewall until you add that subnet to FIREWALL_OUTBOUND_SUBNETS.
Does Gluetun work as a kill switch when the VPN drops?
Yes, and for two reasons at once. The attached container has no route except the one in the shared namespace, so a dead tunnel leaves it with no path off the machine. Gluetun's firewall also allows outbound traffic only through the tunnel and to the VPN server endpoint. Gluetun then restarts the VPN internally, logging WARN [vpn] restarting VPN because it failed to pass the healthcheck, rather than exiting, because every attached container loses its network when gluetun itself restarts.
Tailscale and Gluetun in the same stack: which one carries outbound traffic?
Gluetun, in every configuration except one. Tailscale only routes traffic between devices in your tailnet by default and leaves public traffic alone. In the container image's default userspace mode it creates no interface at all, so it cannot affect routing. With TS_USERSPACE=false it installs routes for 100.64.0.0/10 and your advertised subnets only. The exception is an exit node: sudo tailscale set --exit-node=<exit-node-ip> makes Tailscale the default route, and then it wins. Choose one product to own the default route rather than stacking both.