SSD Nodes Learn Hosting plans →
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-10-24

Docker Compose networking: DNS، host mode اور UFW

Compose کا default bridge، service name سے DNS، host mode کا درست استعمال، projects کے درمیان مشترک network، اور وہ published port جو UFW کو bypass کرتا ہے۔

آپ کی ایپ شروع ہونے سے پہلے Compose کیا بناتا ہے

Docker Compose کی networking ایک اصول سے شروع ہوتی ہے: docker compose up پروجیکٹ کے لیے private network بناتا ہے، ہر service کو اس سے attach کرتا ہے، اور ان services کو service name کے ذریعے ایک دوسرے تک پہنچنے دیتا ہے۔ اس کے لیے آپ کو ایک بھی networks: لائن لکھنے کی ضرورت نہیں ہوتی۔ Compose networking سے متعلق زیادہ تر الجھن اس بات سے پیدا ہوتی ہے کہ default network پہلے ہی موجود ہوتا ہے۔

یہ ایک چھوٹی فائل ہے۔ اسے shop نام کی directory میں compose.yaml کے طور پر محفوظ کریں۔

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

اسے start کریں اور دیکھیں کہ Docker نے کیا بنایا:

docker compose up -d
docker network ls

اب فہرست میں shop_default نام کا network موجود ہے۔ Compose اسے <project>_default نام دیتا ہے، اور project name بطور default directory name کو lowercase میں استعمال کرتا ہے۔ اسے docker compose -p myproject up -d یا فائل میں top-level name: myproject کے ذریعے override کریں۔ اس کا driver bridge ہے، جو host کے اندر ایک virtual switch ہوتا ہے۔ ہر container کو private subnet پر ایک address ملتا ہے، اور باہر جانے والی network traffic کو راستے میں host کے address میں translate کیا جاتا ہے۔

docker compose down اس network کو دوبارہ delete کر دیتا ہے۔ اسی وجہ سے کسی پرانے project کا stale container network کو برقرار رکھ سکتا ہے: Docker error while removing network: network shop_default has active endpoints کے ساتھ انکار کرتا ہے، اور حل یہ ہے کہ اس network سے ابھی تک attached container کو stop یا remove کر دیں۔

اگر Compose آپ کے لیے نیا ہے تو Compose فائل کی ساخت اور lifecycle commands پہلے پڑھ لینا مفید ہے، کیونکہ ذیل کے تمام حصے اس بات کو فرض کرتے ہیں کہ آپ project کو start اور stop کر سکتے ہیں۔

سروس نام کے ذریعے DNS وہ حصہ ہے جسے ابتدائی صارفین نظر انداز کر دیتے ہیں

صارف کی بنائی ہوئی کسی بھی network پر Docker ایک embedded DNS server چلاتا ہے، جسے ہر container 127.0.0.11 پر دیکھتا ہے۔ یہ service names کو موجودہ container addresses میں resolve کرتا ہے۔ اس لیے web بغیر کسی configuration کے hostname db پر database تک، port 5432 کے ذریعے پہنچ جاتا ہے۔

docker compose exec web getent hosts db

یہ 172.18.0.2 db جیسی line دکھاتا ہے۔ اگر کچھ بھی print نہ ہو تو دونوں services ایک ہی network پر نہیں ہیں۔

تقریباً ہر شخص ایک بار یہ غلطی کرتا ہے کہ application config میں localhost استعمال کر لیتا ہے۔ Container کے اندر localhost اسی container کو ظاہر کرتا ہے، host یا دوسری service کو نہیں۔ Postgres clients یہ بات واضح طور پر بتاتے ہیں:

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

Connection string postgresql://postgres:example@db:5432/postgres ہونی چاہیے۔ اس میں host والا حصہ service name ہے۔

بعد میں وقت بچانے والی دو باتیں اہم ہیں۔ Names اس وقت چلنے والے containers کے مطابق resolve ہوتے ہیں، اس لیے docker compose up -d --scale web=3 ایک name کے ساتھ تین addresses دے سکتا ہے۔ جو client DNS کو ہمیشہ cache کرتا رہے گا، وہ خود کو ایک ختم ہو چکے container کے ساتھ جوڑ دے گا۔ دوسری بات یہ ہے کہ plain docker run کے ذریعے استعمال ہونے والا legacy bridge network، جب --network نہ دیا گیا ہو، container names resolve نہیں کر سکتا۔ وہاں containers ایک دوسرے کو صرف IP address کے ذریعے تلاش کرتے ہیں، اور بیرونی hostnames کے لیے host کے /etc/resolv.conf کی ایک copy استعمال کرتے ہیں۔ اسی لیے 2016 کے container links سے متعلق ہدایات آپ کے موجودہ مشاہدے سے مطابقت نہیں رکھتیں۔

دو سروسز کو آپس میں connect کرنے کے لیے ports: کی ضرورت نہیں

ports:، host پر container port publish کرتا ہے۔ یہ Docker کے باہر سے آنے والے network traffic کے لیے ہوتا ہے۔ اس کا service-to-service traffic سے کوئی تعلق نہیں، کیونکہ project network پر پوری port range کے ذریعے یہ traffic پہلے ہی کام کرتا ہے۔

لہٰذا database service میں بہت سے لوگ جو ports: - "5432:5432" شامل کرتے ہیں، وہ بے فائدہ ہونے کے ساتھ حقیقی نقصان بھی پہنچاتا ہے: یہ server کے public interface پر Postgres کو expose کر دیتا ہے۔ اسے حذف کریں۔ اگر migration کے لیے اسے اپنے laptop سے قابل رسائی بنانا ہو تو "127.0.0.1:5432:5432" کے ذریعے اسے loopback پر bind کریں اور SSH tunnel کے ذریعے access کریں۔ listening socket، published port اور firewall rule کے درمیان فرق Linux پر ports اور listening services کیسے کام کرتی ہیں میں بیان کیا گیا ہے۔

Compose کے تحت expose: صرف documentation ہے۔ یہ کچھ open نہیں کرتا، کیونکہ ایک ہی network پر موجود containers کے درمیان کچھ بھی بند نہیں تھا۔

network_mode host کب درست ہے، اور اس کی قیمت کیا ہے

Host mode container کا اپنا network namespace ختم کر دیتا ہے اور process کو host کے interfaces براہ راست استعمال کرنے دیتا ہے۔

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

اسے استعمال کرنے کی حقیقی وجوہات موجود ہیں۔ جو process مقامی network پر broadcast یا multicast traffic دیکھنا چاہتا ہو، مثلاً media server یا home automation hub کے لیے device discovery، وہ bridge کے پیچھے رہ کر یہ traffic نہیں دیکھ سکتا، کیونکہ bridge اسے container تک forward نہیں کرتا۔ جو monitoring agent host کے interface counters پڑھتا ہو، اسے host کے interfaces درکار ہوتے ہیں۔ اس کے علاوہ address translation کا ایک مرحلہ بھی ختم ہو جاتا ہے، جو packet rates زیادہ ہونے پر اہم ہو سکتا ہے۔

اس کے نقصانات مخصوص ہیں۔

ports: کام کرنا بند کر دیتا ہے۔ Docker خبردار کرتا ہے کہ host network mode میں published ports نظر انداز کر دیے جاتے ہیں، اور container وہی bind کرتا ہے جسے اس کا process bind کرے۔ اگر دو host mode containers port 8080 حاصل کرنا چاہیں تو تصادم ہوتا ہے، اور دوسرا container bind: address already in use کے ساتھ بند ہو جاتا ہے۔

دونوں سمتوں میں service name کے ذریعے name resolution ختم ہو جاتی ہے۔ Container project network پر نہیں ہوتا، اس لیے وہ db کو resolve نہیں کر سکتا، اور دوسری services بھی اسے resolve نہیں کر سکتیں۔ پھر بھی یہ host پر published ports کے ذریعے عموماً 127.0.0.1 پر ان تک پہنچ سکتا ہے۔ Linux پر یہ کسی بھی listening port کے ذریعے bridge container کے IP address تک براہ راست بھی پہنچ سکتا ہے، کیونکہ host کو اپنی bridge networks پر موجود containers تک کھلی رسائی حاصل ہوتی ہے اور host mode container host کا network stack استعمال کرتا ہے۔ Containers دوبارہ بنائے جانے پر یہ addresses بدل جاتے ہیں، اس لیے loopback پر published port مستقل route فراہم کرتا ہے۔

Isolation ختم ہو جاتی ہے۔ Host mode container کے اندر 0.0.0.0 کو bind کرنے والا process آپ کے server کے ہر interface پر، public interface سمیت، بالکل اسی طرح listen کر رہا ہوتا ہے جیسے apt کے ذریعے install کیا گیا package۔ اس کا ایک فائدہ بھی ہے: یہ traffic معمول کے input path سے گزرتا ہے، اس لیے UFW rules اس پر لاگو ہوتے ہیں۔ Published ports کے معاملے میں ایسا نہیں ہوتا۔

Host mode ایک Linux Docker Engine feature ہے۔ Docker Desktop اسے صرف version 4.34 سے، اور اسے enable کرنے کے بعد، support کرتا ہے۔ اس کے علاوہ containers host IP addresses کو bind نہیں کر سکتے، اور صرف TCP اور UDP handle کیے جاتے ہیں۔ اگر آپ کی نصف team Linux servers پر اور نصف Docker Desktop پر ہے تو توقع رکھیں کہ ایک ہی file مختلف طریقے سے کام کرے گی۔

Host mode اسی وقت استعمال کریں جب آپ کو host کے interfaces درکار ہوں۔ Connection problem حل کرنے کے لیے اسے استعمال نہ کریں، کیونکہ یہ عموماً ایک مسئلے کو اس سے زیادہ پیچیدہ مسئلے سے بدل دیتا ہے۔

دو Compose projects کو external network سے مربوط کریں

ایک project کے بنائے ہوئے network کو دوسرا project نہیں دیکھ سکتا۔ اسی لیے proxy/compose.yaml میں موجود reverse proxy، اسی server پر ہونے کے باوجود، app/compose.yaml میں موجود app تک نہیں پہنچ سکتا۔ اس کا حل ایسا network ہے جس کی ملکیت کسی بھی project کے پاس نہ ہو۔

اسے ایک مرتبہ دستی طور پر بنائیں:

docker network create edge

پھر ہر project میں اسے external کے طور پر declare کریں۔ Proxy کا حصہ:

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

Application کا حصہ:

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

external: true Compose کو نیا network بنانے کے بجائے موجودہ network سے attach ہونے کی ہدایت دیتا ہے، اور docker compose down پر اسے برقرار رکھتا ہے۔ الگ name: key کی اہمیت بظاہر کم لگتی ہے، لیکن یہ ضروری ہے: اس کے بغیر Compose بالکل edge نام کے network کو تلاش کرتا ہے، جبکہ اس کے ذریعے آپ اپنی file میں network کا ایک نام اور host پر دوسرا نام استعمال کر سکتے ہیں۔

اگر network موجود نہ ہو تو Compose start ہونے سے انکار کر دیتا ہے اور بتاتا ہے کہ network کو external کے طور پر declare کیا گیا تھا، لیکن وہ نہیں ملا۔ اسے پہلے بنائیں۔

غور کریں کہ application file internal کے ساتھ کیا کرتی ہے۔ Database صرف اسی project-local network پر موجود ہے، اس لیے proxy اس تک نہیں پہنچ سکتا اور صرف app اس تک رسائی حاصل کر سکتا ہے۔ کسی network کے تحت internal: true شامل کرنے سے یہ ایک قدم آگے بڑھتا ہے اور اس کا بیرونی دنیا تک route مکمل طور پر ختم کر دیتا ہے۔ Database کے لیے یہ ایک اچھا default ہے، لیکن اسے set کرنے سے پہلے ایک اہم قیمت سمجھ لیں: internal network پر موجود container کوئی چیز download نہیں کر سکتا۔ اس لیے startup کے وقت apt-get update یا pip install چلانے والا entrypoint hang ہو جائے گا اور پھر timeout کے ساتھ fail ہو جائے گا۔

Routing rules اور certificates کے ساتھ مکمل عملی setup کے لیے ایک Traefik instance کے پیچھے متعدد apps چلانا دیکھیں۔

شائع کردہ ports، UFW کو bypass کرتے ہیں

Compose networking کا یہ وہ حصہ ہے جو security incident میں تبدیل ہو سکتا ہے۔ آپ ایک port publish کرتے ہیں، تصدیق کرتے ہیں کہ UFW فعال ہے اور SSH کے علاوہ ہر چیز کو deny کر رہا ہے، لیکن service پھر بھی internet سے reachable رہتی ہے۔

sudo ufw status
curl http://203.0.113.10:8080

UFW کے مطابق port blocked ہے۔ curl پھر بھی page واپس کرتا ہے۔ کچھ بھی خراب نہیں ہے۔ Docker اپنی address translation اور forwarding rules براہِ راست iptables میں لکھتا ہے۔ Published container port پر آنے والا traffic host تک پہنچنے کے بجائے container کو forward کر دیا جاتا ہے۔ اس لیے وہ اس chain سے نہیں گزرتا جسے UFW مقامی طور پر destined traffic کے لیے manage کرتا ہے۔ Docker کی rules بھی UFW سے پہلے match ہوتی ہیں۔

مختصر حل یہ ہے کہ port صرف وہاں publish کریں جہاں اس کی ضرورت ہو:

    ports:
      - "127.0.0.1:8080:80"

اس سے host side کو loopback سے bind کر دیا جاتا ہے۔ یوں port خود server سے اور SSH tunnel کے ذریعے reachable رہتا ہے، لیکن کسی اور جگہ سے نہیں۔ Public entry point کو reverse proxy کے پیچھے رکھیں، جو جان بوجھ کر 80 اور 443 publish کرے۔ مکمل وضاحت، بشمول ان صورتوں کے لیے DOCKER-USER chain جن میں published port کو filter کرنا ضروری ہو، Docker UFW کو bypass کرتے ہوئے براہِ راست publish کیوں کرتا ہے اور اسے کیسے درست کریں میں موجود ہے۔

چار commands سے debugging کا طریقہ

سب سے پہلے معلوم کریں کہ ہر container حقیقت میں کس network پر ہے:

docker network inspect shop_default

Containers block ہر attached container کو اس کے address کے ساتھ دکھاتا ہے۔ اس فہرست میں موجود نہ ہونے والی service کسی دوسرے network پر، host mode میں، یا غیر فعال ہو سکتی ہے۔

Name resolution کو اسی network سے attached ایک عارضی container سے test کریں، تاکہ اپنی images کے اندر کسی tooling کی ضرورت نہ پڑے:

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

nslookup کا fail ہونا name resolution یا network membership کی طرف اشارہ کرتا ہے۔ nslookup کامیاب ہو لیکن nc fail ہو تو name اور network درست ہیں اور مسئلہ خود connection میں ہے۔ اگر nc، Connection refused report کرے تو عام وجہ یہ ہوتی ہے کہ service اس port پر listen نہیں کر رہی، یا اپنے container کے اندر 127.0.0.1 پر listen کر رہی ہے، 0.0.0.0 پر نہیں۔ Development servers میں یہ آخری مسئلہ عام ہے۔ اس کا حل application کے bind address میں ہے، Docker میں نہیں۔ اگر nc hang ہو کر timeout ہو جائے تو دیکھیں کہ راستے میں کوئی چیز traffic drop تو نہیں کر رہی، مثلاً firewall rule، یا کوئی ایسا container جو paused ہو یا جواب نہ دے رہا ہو۔

ایک اور failure Docker bug جیسا دکھائی دیتا ہے۔ اگر containers ایک دوسرے سے بات کر سکتے ہوں لیکن office یا VPN network کی کسی machine تک نہیں پہنچ سکتے، تو غالباً Docker subnet اس network کے ساتھ overlap کر رہا ہے۔ Docker default طور پر 172.17.0.0/16 سے اوپر addresses allocate کرتا ہے۔ Pool کو /etc/docker/daemon.json میں منتقل کریں:

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

اس کے بعد sudo systemctl restart docker چلائیں اور متاثرہ networks دوبارہ بنائیں، کیونکہ موجودہ network وہی subnet برقرار رکھتا ہے جس کے ساتھ اسے بنایا گیا تھا۔

FAQ

میرے containers ایک دوسرے تک service name کے ذریعے کیوں نہیں پہنچ سکتے؟

وہ ایک ہی network پر نہیں ہیں۔ Compose ہر service کو خودکار طور پر <project>_default پر رکھتا ہے، لیکن جیسے ہی آپ کسی service میں networks: فہرست شامل کرتے ہیں، وہ فہرست اس service کے لیے networks کا مکمل مجموعہ بن جاتی ہے اور default network خودکار طور پر شامل نہیں رہتا۔ docker network inspect <network> چلائیں اور چیک کریں کہ دونوں containers Containers block میں موجود ہیں۔ یہ بھی چیک کریں کہ کسی service میں network_mode: host استعمال نہ ہو، کیونکہ host mode container کسی Docker network پر نہیں ہوتا اور service names resolve نہیں کر سکتا۔

کیا ایک service کو دوسری service تک پہنچنے کے لیے ports publish کرنا ضروری ہے؟

نہیں۔ Compose network پر ہر container کا ہر port اسی network کے دوسرے containers کے لیے قابل رسائی ہوتا ہے۔ ports: صرف container کو Docker کے باہر سے آنے والے network traffic کے لیے expose کرنے کے لیے ہوتا ہے، جبکہ expose: documentation ہے۔ Database port publish کرنا ایک عام اور مہنگی عادت ہے، کیونکہ اس سے database آپ کے server کے public interface پر آ جاتا ہے۔

bridge اور host networking میں کیا فرق ہے؟

Bridge container کو virtual switch پر اپنا network namespace اور address دیتا ہے۔ اس میں containers کے درمیان خودکار name resolution اور outbound traffic translation بھی شامل ہوتی ہے۔ Host container کو براہ راست host کا network stack دیتا ہے۔ اس میں الگ address، service name کے ذریعے resolution، port publishing یا host کے دوسرے listeners سے isolation نہیں ہوتی۔ Bridge default انتخاب ہے اور عموماً یہی درست ہے، الا یہ کہ process کو host کے interfaces درکار ہوں۔

میں دو مختلف Compose files کے containers کو کیسے connect کروں؟

docker network create edge کے ذریعے shared network بنائیں۔ پھر دونوں files میں اسے external: true کے ذریعے declare کریں اور ان services کو اس network سے attach کریں جنہیں ایک دوسرے سے رابطہ کرنا ہے۔ Compose اس network کو نہ create کرے گا اور نہ delete۔ اگر create کرنے کا مرحلہ چھوڑ دیں تو Compose start ہونے سے انکار کر دے گا اور network کو declared external but not found کے طور پر رپورٹ کرے گا۔

UFW port block کرتا ہے، پھر میرا container internet سے کیوں قابل رسائی ہے؟

کیونکہ published port کو Docker کی طرف سے iptables میں شامل کی گئی forwarding rules handle کرتی ہیں۔ یہ rules UFW کی rules سے پہلے match ہوتی ہیں، اور forwarded traffic اس chain سے گزرتا ہی نہیں جسے UFW filter کرتا ہے۔ Host side کو "127.0.0.1:8080:80" کے ذریعے loopback پر bind کریں، اور public services کو ports 80 اور 443 پر reverse proxy کے پیچھے رکھیں۔

کیا یہ مضمون مفید تھا؟