SSD Nodes Learn 🎉 VPS من $4.99/شهر
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-07

Gluetun: كيف تصل إلى المضيف والحاويات الأخرى

تظهر رسالة الخطأ لأن الحاوية خلف Gluetun لا تملك واجهات مستقلة. انشر منافذها على Gluetun، وافتح فقط الشبكات الفرعية المطلوبة خارج النفق.

ماذا يحدث عند انضمام حاوية إلى شبكة gluetun

لا تملك الحاوية التي تضبط network_mode: service:gluetun أي واجهات شبكة خاصة بها. تنضم إلى مساحة أسماء الشبكة الخاصة بـgluetun، لذلك لا يعود نشر المنافذ وقواعد الجدار الناري من خصائص تلك الحاوية، بل يصبحان من خصائص خدمة gluetun. وتنتج كل الإجابات أدناه عن هذه الحقيقة وحدها.

مساحة أسماء الشبكة هي نسخة خاصة من مكدس الشبكة ينشئها kernel: لها واجهاتها الخاصة، وجدول التوجيه الخاص بها، وقواعد الجدار الناري الخاصة بها، ومقابس الاستماع الخاصة بها. يمنح Docker كل حاوية مساحة أسماء خاصة بها افتراضياً. عند كتابة network_mode: service:gluetun، يتجاوز Docker هذه الخطوة ويضع الحاوية الجديدة داخل مساحة الأسماء التي تملكها gluetun بالفعل. تحتفظ الحاوية بنظام ملفاتها الخاص وملف /etc/hosts الخاص بها، وستصبح هذه النقطة الثانية مهمة لاحقاً.

يمكنك رؤية ذلك مباشرةً.

docker inspect -f '{{.HostConfig.NetworkMode}}' qbittorrent

يعرض ذلك container: متبوعاً بمعرّف حاوية gluetun، بينما كانت الحاوية العادية ستعرض bridge. يتابع هذا الدليل من حيث ينتهي توجيه حركة Docker عبر VPN باستخدام gluetun: يعمل النفق، والآن لا يستطيع أي شيء الاتصال بالحاوية.

نشر المنفذ على gluetun، وليس على التطبيق

اترك كتلة ports: في الخدمة التي تضبط network_mode، وسيرفض Docker إنشاء الحاوية:

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

السبب مباشر. نشر منفذ يعني إضافة قاعدة NAT (ترجمة عناوين الشبكة) تعيد توجيه منفذ من المضيف إلى مساحة أسماء الشبكة الخاصة بحاوية، لكن هذه الحاوية لا تملك مساحة أسماء خاصة بها. انقل الربط إلى خدمة gluetun. لا يتغير رقم المنفذ، لأن التطبيق ما زال يستمع على ذلك المنفذ داخل مساحة الأسماء المشتركة.

services:
  gluetun:
    ports:
      - "8080:8080/tcp"   # qBittorrent web UI

  qbittorrent:
    network_mode: "service:gluetun"
    # no ports: block here

وجود كتلة expose: في الخدمة التابعة لا فائدة منه أيضاً، بينما وجود كتلة networks: فيها يمنع التشغيل تماماً: يوضح Compose أن الخدمة تعرّف network_mode وnetworks المتعارضين، ويرفض تحميل الملف بالكامل.

تظهر نتيجة أخرى لاحقاً. تشترك جميع الحاويات في مساحة الأسماء نفسها في مساحة منافذ واحدة، لذلك تتعارض أي تطبيقات تستخدم 8080 افتراضياً. ويفشل التطبيق الثاني الذي يبدأ برسالة تفيد بأن العنوان قيد الاستخدام. غيّر المنفذ في إعدادات أحد التطبيقين، مثلاً متغير WEBUI_PORT في صورة LinuxServer qBittorrent، ثم انشر الرقم الجديد على gluetun.

كيف تصل الحاويات خلف gluetun إلى بعضها؟

تتشارك هذه الحاويات بالفعل واجهة loopback داخل مساحة الأسماء. تصل الحاوية الموجودة خلف gluetun إلى الحاوية الشقيقة على 127.0.0.1:<port> من دون استخدام أي شبكة Docker.

من خارج مساحة الأسماء، لا يكون للحاوية اسم. يحل DNS المضمّن في Docker اسم الخدمة إلى عنوان تلك الخدمة على شبكة يعرّفها المستخدم، لكن هذه الحاوية لا تملك عنواناً على أي شبكة. لذلك لا تصل حاوية عادية مثل Sonarr إلى عميل التورنت على http://qbittorrent:8080. بل تصل إليه على http://gluetun:8080، لأن socket يستمع في مساحة أسماء gluetun وعلى عنوان gluetun. يفاجئ ذلك المستخدمين الذين يعرفون طريقة عمل شبكات Docker Compose وأسماء الخدمات ويتوقعون تطبيق التسمية المعتاد. ويعمل ذلك أيضاً من دون نشر أي منفذ على المضيف، لأن الحاويتين موجودتان على شبكة Compose نفسها.

تحقق من DNS قبل تصحيح أي مشكلة أخرى. يشغّل Gluetun محلّل أسماء خاصاً به ويعيد كتابة /etc/resolv.conf داخل حاويته، لكن /etc/resolv.conf ملف مستقل لكل حاوية، لذلك فإن الملف الذي كتبه gluetun ليس الملف الذي يقرأه تطبيقك.

docker exec qbittorrent cat /etc/resolv.conf

كيف أصل إلى خدمة تعمل على مضيف Docker؟

استخدم host.docker.internal. يتطلب ذلك إعدادين في موضعين مختلفين، لأن هناك مشكلتين مختلفتين.

يأتي الاسم أولاً. /etc/hosts خاص بكل حاوية، لذلك يجب وضع إدخال extra_hosts في حاوية التطبيق، وليس في gluetun.

  prowlarr:
    network_mode: "service:gluetun"
    extra_hosts:
      - "host.docker.internal:host-gateway"

host-gateway قيمة خاصة يستبدلها Docker بعنوان داخلي للمضيف نفسه. في تثبيت Docker عادي على Linux، يكون هذا عنوان جسر docker0، وغالباً ما يكون 172.17.0.1. تحقّق من العنوان لديك باستخدام ip -4 addr show docker0 على VPS. يحل Docker Desktop هذا الاسم تلقائياً، ولذلك تتجاوز الأدلة المكتوبة للعمل على جهاز محمول سطر extra_hosts، ثم يفشل الملف نفسه على الخادم.

يأتي المسار ثانياً. إضافة الاسم تخبر الحاوية فقط بالعنوان الذي يجب استخدامه. تظل الحزمة تخرج عبر المسار الافتراضي لـ gluetun، وهو النفق، ثم يحظرها جدار gluetun الناري. يتمثل العرض في تعليق الاتصال ثم انتهاء مهلة الانتظار، وليس رفض الاتصال. الرفض يعني أن الحزمة وصلت وأن شيئاً ما ردّ بالرفض. أما انتهاء المهلة فيعني أنها لم تصل.

  gluetun:
    environment:
      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32

بعد ذلك، تحقّق من أن خدمة المضيف تستمع فعلياً على ذلك العنوان. لا يمكن الوصول إلى خادم PostgreSQL المربوط بـ 127.0.0.1 فقط من أي حاوية، سواء عبر نفق أو بدونه، لأن 127.0.0.1 داخل مساحة الأسماء هو loopback الخاص بمساحة الأسماء نفسها. اربطه بـ 172.17.0.1 بدلاً من ذلك؛ فهو يقبل الاتصالات من الحاويات مع بقائه غير متاح على الواجهة العامة. تحقّق من ذلك باستخدام ss -lntp | grep 5432 على المضيف.

ما الذي يغيّره FIREWALL_OUTBOUND_SUBNETS فعلياً

تصف وثائق gluetun هذه القيمة بأنها الشبكات الفرعية المفصولة بفواصل التي يُسمح لـ gluetun والحاويات التي تشاركه مكدس الشبكة بالوصول إليها، وتوضح أنها تتضمن تغييرات في الجدار الناري والتوجيه. كلا الجزأين مهم. يضيف gluetun مساراً لكل شبكة فرعية مدرجة عبر بوابة جسر Docker، لذلك تخرج الحزم المتجهة إلى تلك العناوين عبر eth0 بدلاً من النفق. كما يفتح الجدار الناري أمامها، لأن gluetun يسقط بخلاف ذلك حركة المرور الصادرة التي لا تتجه إلى خادم VPN.

اكتب القيمة من دون مسافات بعد الفواصل.

      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,192.168.1.0/24,100.64.7.9/32

من السهل إغفال خاصيتين. هذا إعداد على مستوى مساحة أسماء الشبكة، لذلك ينطبق على كل حاوية خلف gluetun، وليس على الحاوية التي تقصدها فقط. وهو خاص بالاتصالات الصادرة فقط: يتحكم في الاتصالات التي تبدأها الحاوية. أما الاتصالات الواردة إلى منفذ منشور فتسلك مساراً مختلفاً، ولا تحتاج إلى إدخال هنا.

الوصول إلى واجهة الويب من نظير Tailscale

يمنح Tailscale كل جهاز عنواناً ضمن 100.64.0.0/10، وهو النطاق المحجوز لـ Carrier-Grade NAT. ويتطلب الاتجاهان إعدادين مختلفين.

الوصول الوارد هو الأبسط. يؤدي نشر 8080:8080 على gluetun إلى ربط هذا المنفذ بجميع عناوين المضيف، وتُعد واجهة tailscale0 في المضيف أحد هذه العناوين. لذلك يفتح النظير http://<machine-name>:8080 ويصل إلى الحاوية. لا يشارك Gluetun في هذا المسار، لأن قاعدة NAT الخاصة بـDocker موجودة على المضيف وخارج مساحة الأسماء.

لجعل واجهة المستخدم قابلة للوصول عبر tailnet فقط، اربط المنفذ المنشور بعنوان Tailscale الخاص بالمضيف بدلاً من ربطه بجميع العناوين.

    ports:
      - "100.101.102.103:8080:8080/tcp"

اعثر على هذا العنوان باستخدام tailscale ip -4 على المضيف. يُعد الربط هنا وسيلة تحكم أقوى من قاعدة جدار ناري، لأن المنفذ لا يُفتح على الواجهة العامة أصلاً. كما يتجاوز المشكلة الموضحة في نشر Docker للمنافذ مباشرةً متجاوزاً ufw.

يظهر الاتجاه الصادر عندما يعود FIREWALL_OUTBOUND_SUBNETS. إذا احتاجت الحاوية إلى الاتصال بنظير، فأضف عنوان ذلك النظير، وفضّل استخدام /32 لكل نظير بدلاً من النطاق /10 بأكمله. لن تُحل أسماء MagicDNS داخل الحاوية، لأن الحاوية لا تستخدم محلل الأسماء في المضيف. لذلك استخدم عنوان 100.x الرقمي أو ثبّته باستخدام سطر extra_hosts. وينطبق الأمر نفسه عند تشغيل خادم Tailscale للتحكم بنفسك باستخدام Headscale.

ملف Compose كامل للبنية الشائعة

عميل تنزيل خلف VPN، وواجهتا ويب تستجيبان على tailnet فقط، وحاوية واحدة تقرأ قاعدة بيانات PostgreSQL تعمل على المضيف.

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-stopped

اقرأ الملف لفهم النمط، وليس لأسماء المنتجات. تُنشر واجهتا الويب كلتاهما على gluetun وتُربطان بعنوان tailnet الخاص بالمضيف، لذلك تستجيبان عبر Tailscale فقط ولا تستجيبان من أي مكان آخر. يحتوي Prowlarr وحده على السطر extra_hosts، لأن Prowlarr هو الحاوية التي تحل host.docker.internal. يعرّف FIREWALL_OUTBOUND_SUBNETS عنوانين منفردين: عنوان Docker bridge الخاص بالمضيف، لكي يتمكن Prowlarr من فتح اتصال بقاعدة البيانات، وعنوان أحد أقران tailnet.

خادم PostgreSQL غير موجود عمداً في الملف. فهو يعمل على VPS كخدمة نظام عادية وتستمع إلى 172.17.0.1:5432. هذه هي الطبقات نفسها المستخدمة في حزمة arr على Docker Compose، مع نقل قاعدة البيانات إلى خارج Docker.

أبقِ المفتاح الخاص لـWireGuard خارج ملف Compose. يقرأ ${WIREGUARD_PRIVATE_KEY} من ملف .env موجود بجانبه، وفق النمط الموضح في ملفات البيئة والأسرار لـDocker Compose. يستخدم الشرط condition: service_healthy healthcheck الذي تأتي به صورة gluetun مسبقاً، لذلك لا يبدأ أي شيء حتى يعلن النفق عن عمله. توضّح فحوصات صحة Compose الصيغة العامة.

النشر على كل عنوان بدلاً من tailnet فقط

احذف بادئة العنوان وربطَي المنفذ في 0.0.0.0، إذ يشمل ذلك عنوان VPS العام. نفّذ ذلك فقط خلف جدار ناري تتحكم فيه، واقرأ ملاحظة ufw أعلاه أولاً.

    ports:
      - "8080:8080/tcp"

تأكد من أن النفق ما زال ينقل حركة الشبكة

نفّذ الطلب نفسه مرتين، مرة من داخل مساحة الأسماء ومرة من المضيف، ثم قارن النتيجتين.

docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org
curl -s https://api.ipify.org

يجب أن يعرض الطلب الأول عنوان الخروج الخاص بموفّر VPN لديك. ويجب أن يعرض الطلب الثاني عنوان VPS. إذا تطابق العنوانان، فهذا يعني أن حركة الشبكة من الحاوية لا تمر عبر النفق. ولن تفيد أي خطوة إصلاح في هذا الدليل قبل تصحيح ذلك.

يعرض جدول التوجيه ما يمر خارج النفق وما لا يمر عبره.

docker run --rm --network=container:gluetun alpine:3.22 ip route show

يجب أن يشير المسار الافتراضي إلى واجهة النفق، tun0. وسترى تحته مساراً واحداً لكل إدخال في FIREWALL_OUTBOUND_SUBNETS، يشير إلى بوابة Docker bridge. وأي مسار آخر يخرج عبر eth0 هو حركة شبكة تتجاوز VPN.

يعرض خادم التحكم في Gluetun عنوان IP العام نفسه على المنفذ 8000، في /v1/publicip/ip. تتطلب الإصدارات الحديثة ضبط المصادقة لمسارات خادم التحكم، لذا أعدّ ذلك قبل الاعتماد عليه.

التسرّب الناتج عن إنشاء شبكة فرعية واحدة بشكل خاطئ

FIREWALL_OUTBOUND_SUBNETS هو ثقب تفتحه عمداً في جدار الحماية، لذلك يساوي حجم الثقب حجم الخطر. فيما يلي أربع طرق لجعله أكبر من اللازم:

  • 0.0.0.0/0 يرسل كل شيء خارج النفق. يكتشف فحصا IP أعلاه ذلك عند التشغيل الأول، لأنهما سيعيدان العنوان نفسه.
  • نطاق أوسع من الهدف. إن فتح 10.0.0.0/8 للوصول إلى جهاز واحد على 10.0.1.7 يفتح أيضاً كل عنوان قد يعلنه أحد أقران torrent ضمن ذلك النطاق. اكتب 10.0.1.7/32.
  • نطاق يتداخل مع عناوين النفق نفسه. تحذّر وثائق gluetun من أن ذلك يجعل gluetun يرسل حركة VPN عبر الجسر بدلاً من النفق، ما يعطّل إعادة توجيه المنافذ. تحقّق من قيمة WIREGUARD_ADDRESSES قبل فتح أي نطاق خاص.
  • 100.64.0.0/10 لـ Tailscale. هذا يفتح نحو أربعة ملايين عنوان حتى يمكن الوصول إلى نظير واحد. أدرج الأقران الذين تحتاج إليهم كإدخالات /32.

تذكّر أن هذا الإعداد ينطبق على مساحة الأسماء بأكملها. إن فتح شبكة فرعية حتى يتمكن indexer من الوصول إلى خدمة على المضيف يفتح الشبكة الفرعية نفسها لعميل torrent الذي يشارك مساحة الأسماء تلك. أعد فحص IP العام بعد كل تغيير في هذا المتغير، لأنه الاختبار الوحيد الذي يوضح ما إذا كان التغيير قد حقق ما قصدته.

ما الذي يتعطل عند إعادة تشغيل gluetun

يمتلك gluetun مساحة الأسماء، لذلك تتبع دورة حياة gluetun دورة حياة مساحة الأسماء. ويفشل تشغيل حاوية تعتمد عليه فوراً عندما يكون gluetun متوقفاً:

Error response from daemon: cannot join network of a non running container

تُعد إعادة تشغيل gluetun في مكانه الحالة الأكثر هدوءاً من حيث ظهور الفشل. تبقى الحاويات التابعة قيد التشغيل بينما يُعاد بناء مساحة الأسماء التي اتصلت بها من تحتها، لذلك يعرض docker ps أن كل شيء سليم، ولا يستجيب أي شيء. بعد إجراء أي تغيير على خدمة gluetun، أعد إنشاء المجموعة بأكملها بدلاً من إعادة تشغيل جزء واحد منها.

docker compose up -d --force-recreate

ينطبق الأمر نفسه على تحديثات الصور. يؤدي سحب صورة gluetun جديدة وإعادة إنشاء هذه الخدمة وحدها إلى ترك الخدمات الأخرى متصلة بمساحة أسماء لم تعد موجودة.

FAQ

لماذا يعرض Docker رسالة "port publishing and the container type network mode"؟

لأن كتلة ports: ما زالت موجودة في خدمة تضبط network_mode: service:gluetun أيضاً. يؤدي نشر منفذ إلى إضافة قاعدة NAT تعيد توجيه منفذ من المضيف إلى مساحة أسماء الشبكة الخاصة بالحاوية، لكن الحاوية التي تستخدم هذا الوضع لا تملك مساحة أسماء خاصة بها. احذف كتلة ports: من تلك الخدمة، وأضف التعيين نفسه إلى خدمة gluetun. يبقى رقم المنفذ كما هو، لأن التطبيق ما زال يستمع عليه داخل مساحة الأسماء المشتركة.

كيف تصل الحاويات الأخرى إلى خدمة تعمل خلف gluetun؟

تصل الحاويات الموجودة داخل مساحة الأسماء نفسها إلى بعضها عبر 127.0.0.1. أما الحاويات الموجودة خارجها، فتستخدم اسم خدمة gluetun، ولذلك يعمل http://gluetun:8080 بينما لا يعمل http://qbittorrent:8080. لا تملك حاوية التطبيق عنواناً على أي شبكة Docker، ولذلك لا يجد خادم DNS المضمّن عنواناً يحلّ إليه اسمها. لا حاجة إلى نشر منفذ لهذا الغرض، ما دامت الحاويتان تشتركان في شبكة Compose.

ماذا أضع في FIREWALL_OUTBOUND_SUBNETS؟

أضف فقط العناوين التي يجب على حاوية تعمل خلف gluetun بدء اتصال بها، واكتبها بأضيق نطاق ممكن. يُكتب عنوان جهاز واحد بصيغة /32. الإدخالان الشائعان هما مضيف Docker بصيغة 172.17.0.1/32، وإدخال /32 واحد لكل نظير Tailscale تتصل به. لا تضف 0.0.0.0/0 مطلقاً، ولا تضف نطاقاً يتداخل مع عناوين نفق VPN نفسه. لا تحتاج الاتصالات الواردة إلى منفذ منشور إلى أي إدخال هنا.

لماذا لا تستطيع الحاوية حل أسماء MagicDNS الخاصة بـTailscale؟

يعمل MagicDNS عبر توجيه محلل الأسماء في المضيف إلى خادم DNS الخاص بـTailscale، لكن الحاوية لا تستخدم محلل الأسماء في المضيف. بل تستخدم ما يحدده /etc/resolv.conf الخاص بها، وهو إعداد DNS الخاص بـgluetun عند تشغيلها خلفه. تحقّق باستخدام docker exec <container> cat /etc/resolv.conf. استخدم العنوان الرقمي 100.x للنظير، أو ثبّت الاسم بإضافة extra_hosts إلى تلك الحاوية.

كيف أتأكد من أن حركة الشبكة ما زالت تمر عبر VPN؟

نفّذ طلباً واحداً من داخل مساحة الأسماء، ونفّذ الطلب نفسه من المضيف، ثم قارن الإجابتين. يجب أن يعرض docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org عنوان الخروج الخاص بموفر VPN، بينما يعرض curl -s https://api.ipify.org على VPS عنوان VPS. تعني الإجابتان المتطابقتان أن النفق لا ينقل حركة الحاوية. أعد هذا الفحص بعد كل تغيير على FIREWALL_OUTBOUND_SUBNETS.