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

Docker Compose میں bind mount یا named volume؟

Docker Compose میں config کے لیے bind mount اور data کے لیے named volume چنیں۔ permission traps، inspect، backup اور migration کے عملی طریقے جانیں۔

Bind mount یا named volume: مختصر جواب

Docker Compose volumes دو قسم کے ہوتے ہیں، اور انتخاب کا تعلق اس بات سے ہے کہ فائلوں کا مالک کون ہے۔ اپنی لکھی اور پڑھی جانے والی فائلوں، مثلاً configuration، templates اور static sites، کے لیے bind mount استعمال کریں۔ اس application کے زیرِ ملکیت data، مثلاً database files، search indexes اور uploaded media، کے لیے named volume استعمال کریں۔ bind mount میزبان کے ایسے path کی طرف اشارہ کرتا ہے جسے آپ editor میں کھول سکتے ہیں۔ named volume وہ storage ہے جسے Docker آپ کے لیے بناتا اور track کرتا ہے، اور اس تک Docker کے ذریعے رسائی حاصل کی جاتی ہے۔

دونوں ایک service کے اندر اسی volumes: key کے تحت ظاہر ہوتے ہیں، اسی لیے اکثر ان میں خلط ملط ہو جاتا ہے۔ فرق colon کے بائیں جانب موجود حصے میں ہے۔ اگر بائیں جانب . یا / سے شروع ہو تو یہ host path ہے، اس لیے یہ bind mount ہے۔ کوئی دوسری قدر name ہوتی ہے، اس لیے یہ named volume ہے، اور اس name کو top-level volumes: block میں بھی declare کرنا ضروری ہے۔

Compose فائل میں دونوں syntax

services:
  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: changeme
    volumes:
      - pgdata:/var/lib/postgresql/data
  web:
    image: nginx:1.27
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./site:/usr/share/nginx/html:ro

volumes:
  pgdata:

pgdata:/var/lib/postgresql/data ایک named volume ہے۔ ./nginx.conf:/etc/nginx/nginx.conf ایک bind mount ہے، اور :ro اسے read only کے طور پر mount کرتا ہے، جو اس configuration کے لیے درست default ہے جسے container کو کبھی rewrite نہیں کرنا چاہیے۔ اگر top-level volumes: entry شامل نہ ہو تو Compose service "db" refers to undefined volume pgdata کے ساتھ رک جاتا ہے۔

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

docker compose up -d
docker volume ls

volume کا نام pgdata نہیں ہے۔ اس کا نام <project>_pgdata ہے، کیونکہ project name بطور default اس directory کے نام کے برابر ہوتا ہے جس میں compose فائل موجود ہو۔ myapp نام کی directory سے myapp_pgdata بنتا ہے۔ یہ اس لیے اہم ہے کہ directory کا نام بدلنے سے نیا خالی volume بن جاتا ہے، اور یوں محسوس ہوتا ہے کہ application کا data ضائع ہو گیا ہے۔ ایسا نہیں ہوا: پرانا volume اب بھی docker volume ls میں درج ہوتا ہے۔ compose فائل میں name: کے ذریعے نام مقرر کریں، یا COMPOSE_PROJECT_NAME set کریں، اگر directory منتقل ہو سکتی ہو۔ ایسی settings کو اپنی دیگر Compose environment files اور secrets کے ساتھ رکھیں۔

اجازت کے مسائل زیادہ تر bind mounts میں کیوں آتے ہیں

یہ سب سے بڑا عملی فرق ہے، اور اس کی بنیادی وجہ ایک اصول ہے: پہلی بار استعمال کے وقت خالی named volume کو image سے ابتدائی مواد ملتا ہے، جبکہ bind mount کو کبھی نہیں ملتا۔

جب Docker کسی ایسے directory پر خالی named volume mount کرتا ہے جس میں image کے اندر پہلے سے مواد موجود ہو، تو وہ اس مواد کو volume میں copy کر دیتا ہے، اور image کی مقرر کردہ ownership اور modes برقرار رہتے ہیں۔ Official Postgres image کا /var/lib/postgresql/data اپنے postgres user کی ملکیت میں ہوتا ہے، اس لیے volume بھی اسی numeric id کی ملکیت میں بنتا ہے اور database start ہو جاتا ہے۔

یہ ابتدائی copy ہی وہ واحد اضافی سہولت ہے جو named volume کو ملتی ہے۔ Docker volume کے مقام کا انتظام کرتا ہے، لیکن containers اسے عام filesystem operations کے ذریعے پڑھتے اور لکھتے ہیں، اس لیے ownership اور modes اسی طرح نافذ ہوتے ہیں جیسے bind mount پر۔ اگر named volume ایسے path پر mount ہو جسے image میں موجود ہی نہ ہو، یا اسے پہلی بار کسی دوسری image یا user نے بھرا ہو، تو اسے image سے کوئی ownership نہیں ملتی، اور وہاں بھی non-root process کو اجازت سے انکار ہو سکتا ہے۔ ذیل کا id check دونوں اقسام کے لیے کام کرتا ہے۔

bind mount اس کے برعکس کام کرتا ہے۔ Host پر جو کچھ موجود ہو، container وہی دیکھتا ہے، ownership سمیت، اور اس path پر موجود image content پوشیدہ ہو جاتا ہے۔ اگر host directory موجود نہ ہو تو Docker daemon اسے بناتا ہے، اور daemon root کے طور پر چلتا ہے، اس لیے آپ کو root:root کی ملکیت والا directory ملتا ہے۔ اس کے بعد non-root user کے طور پر چلنے والا container process اس میں نہیں لکھ سکتا:

PermissionError: [Errno 13] Permission denied: '/data/app.db'

حل یہ ہے کہ numeric values کو ایک جیسا کیا جائے۔ bind mount کے ذریعے ownership کا موازنہ نام سے نہیں بلکہ numeric user id سے کیا جاتا ہے، کیونکہ container کا اپنا /etc/passwd ہوتا ہے۔ Container کے اندر app نام کا user host پر کوئی معنی نہیں رکھتا۔ Uid 1000 دونوں طرف uid 1000 ہی کا مطلب رکھتا ہے، سوائے اس صورت کے جب daemon user-namespace remapping کے ساتھ چل رہا ہو (userns-remap، جو /etc/docker/daemon.json میں یا dockerd command line پر set ہوتا ہے)۔ Remapping ہر container id کو host پر subordinate range میں منتقل کرتا ہے۔ Docker کی مثال میں 231072 سے شروع ہونے والی range کے اندر container uid 0، host uid 231072 ہوتا ہے، اس لیے container uid 1000، host uid 232072 ہوتا ہے۔

id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./data

docker compose exec web id وہ uid دکھاتا ہے جس کے طور پر container process حقیقتاً چل رہا ہے۔ Remapping کے بغیر host directory کو اسی number کی ملکیت میں کریں، یا service میں user: "1000:1000" کے ذریعے container کو اپنے number پر مقرر کریں۔ Remapping فعال ہونے پر user: container کو آپ کے host number پر مقرر نہیں کر سکتا، کیونکہ مقرر کیا گیا id بھی منتقل ہو جاتا ہے: اس مثال کی range میں user: "1000:1000" پھر بھی host uid 232072 پر پہنچتا ہے۔ اس کے بجائے host directory کو mapped host id کی ملکیت میں کریں (آپ کی اپنی range /etc/subuid اور /etc/subgid میں موجود ہے)۔ Docker کی documentation کے مطابق کسی ایک container کے لیے remapping بند کرنے کا طریقہ صرف --userns=host کو docker container create، run یا exec پر استعمال کرنا ہے۔ اپنی لکھی ہوئی application کے لیے user: مقرر کرنا زیادہ صاف حل ہے۔ ایسی image کے لیے جسے آپ نے خود نہیں لکھا، host directory کی ownership تبدیل کرنا زیادہ محفوظ ہے، کیونکہ کچھ images entrypoint کو root کے طور پر شروع کرتی ہیں، پھر privileges کم کرتی ہیں، اور اندر مخصوص ownership کی توقع رکھتی ہیں۔

دو مزید مسائل بھی ذہن میں رکھیں۔ Fedora، RHEL اور SELinux (security-enhanced Linux) enforcing والے دیگر systems پر bind mount کو relabel کیے بغیر اجازت نہیں ملتی، اس لیے containers کے درمیان shared path کے لیے :z یا صرف ایک container کے لیے استعمال ہونے والے path کے لیے :Z شامل کریں، اور اسے - ./data:/data:Z کی صورت میں لکھیں۔ اسی طرح directory کے بجائے کسی ایک file کا bind mount اس وقت ناکام ہو جاتا ہے جب editor file کو in-place لکھنے کے بجائے اسے replace کر دیتا ہے، کیونکہ mount اصل inode کو follow کرتا ہے۔ Container پرانا content دیکھتا رہتا ہے جب تک آپ اسے restart نہ کریں۔ اگر file اکثر edit ہوتی ہے تو parent directory mount کریں۔

کارکرد: جہاں حقیقی فرق موجود ہے

Linux server پر دونوں اقسام kernel کے ایک ہی راستے سے گزرتی ہیں، اس لیے throughput کا فرق اتنا کم ہوتا ہے کہ انتخاب اس بنیاد پر نہیں کرنا چاہیے۔ Default local driver استعمال کرنے والے named volumes، Docker کے باقی ڈیٹا کے ساتھ اسی filesystem پر /var/lib/docker/volumes/ کے تحت موجود ہوتے ہیں، جبکہ bind mount اسی جگہ موجود ہوتا ہے جہاں آپ نے اسے متعین کیا ہو۔

اصل فرق Docker Desktop for macOS اور Windows پر ظاہر ہوتا ہے، جہاں containers ایک virtual machine کے اندر چلتے ہیں۔ وہاں bind mount host filesystem سے file sharing layer کے ذریعے اس virtual machine میں جاتا ہے۔ بہت سی چھوٹی file operations والے workloads، جیسے Node.js dependency tree یا PHP framework cache، نمایاں طور پر سست ہو جاتے ہیں۔ Named volumes virtual machine کے اندر ہی رہتے ہیں اور یہ اضافی لاگت برداشت نہیں کرتے۔ اسی لیے development compose files میں source directory کو bind-mount کیا جاتا ہے، لیکن node_modules پر named volume declare کیا جاتا ہے۔

دوسرا حقیقی فرق یہ ہے کہ bytes کہاں محفوظ ہوتے ہیں۔ /mnt/backup پر bind mount ڈیٹا اسی disk پر رکھتا ہے۔ Named volume اس filesystem پر محفوظ ہوتا ہے جس میں /var/lib/docker موجود ہو؛ VPS پر یہ عموماً root disk ہوتی ہے۔ Named volume کے اندر بڑھتا ہوا database اسی disk کو بھر دیتا ہے جس پر system logs بھی موجود ہوتے ہیں۔ مسئلہ بننے سے پہلے اسے چیک کریں:

docker system df -v
df -h /var/lib/docker

docker system df -v ہر volume کو اس کے size کے ساتھ فہرست کرتا ہے، اور ان volumes کو نشان زد کرتا ہے جنہیں اب کوئی container استعمال نہیں کرتا۔

نام زدہ volume کا معائنہ

نام زدہ volume کوئی بند ڈبہ نہیں ہے۔ Docker سے معلوم کریں کہ یہ کہاں موجود ہے:

docker volume inspect myapp_pgdata

Mountpoint فیلڈ میزبان کا حقیقی path دکھاتا ہے، جو عموماً /var/lib/docker/volumes/myapp_pgdata/_data ہوتا ہے۔ آپ اسے sudo ls کے ذریعے پڑھ سکتے ہیں، اور فوری جانچ کے لیے یہ مفید ہے۔ اسے files میں ترمیم کرنے کی جگہ نہ سمجھیں۔ وہاں root کے طور پر لکھنے سے اوپر بیان کیا گیا ownership کا مسئلہ دوبارہ پیدا ہو جاتا ہے، اور یہ path local driver کی ایک تفصیل ہے جسے دوسرے volume drivers share نہیں کرتے۔

اندر موجود data دیکھنے کا محفوظ طریقہ یہ ہے کہ ایسا عارضی container چلایا جائے جو volume کو mount کرے:

docker run --rm -v myapp_pgdata:/vol alpine ls -la /vol

یہ کسی بھی driver کے ساتھ کام کرتا ہے، وہی permissions دیکھتا ہے جو اصل container دیکھتا ہے، اور --rm کی وجہ سے پیچھے کچھ نہیں چھوڑتا۔

ہر قسم کا بیک اپ

Bind mount ایک عام directory ہوتی ہے، اس لیے file-level backup tool اسے پہلے ہی handle کر لیتا ہے۔ Backup کو host path پر point کریں اور کام مکمل ہو جاتا ہے۔ Named volume کے لیے ایک اضافی مرحلہ درکار ہوتا ہے، کیونکہ tool کو اس کے اندر access کرنا ہوتا ہے۔ Volume اور host directory کو اسی مختصر مدتی container میں mount کریں، پھر archive لکھیں:

docker run --rm \
  -v myapp_pgdata:/data:ro \
  -v "$PWD":/backup \
  alpine tar czf /backup/pgdata.tar.gz -C /data .

Restore کے لیے یہی عمل ایک نئے volume میں الٹی سمت میں انجام دیں:

docker volume create myapp_pgdata_restored
docker run --rm \
  -v myapp_pgdata_restored:/data \
  -v "$PWD":/backup \
  alpine tar xzf /backup/pgdata.tar.gz -C /data

جب tar container کے اندر root کے طور پر چلتا ہے تو numeric ownership برقرار رہتی ہے۔ اسی وجہ سے restored volume application کے لیے قابلِ استعمال رہتا ہے۔

دونوں اقسام کے لیے ایک تنبیہ لاگو ہوتی ہے۔ Database کے چلنے کے دوران اس کی files copy کرنے سے ایسا archive بنتا ہے جو مسلسل تبدیل ہونے والی حالت کی نمائندگی کرتا ہے، اور اسے restore کرنے پر data corrupt ہو سکتا ہے۔ پہلے service روکیں، یا database کے اپنے tool کے ذریعے dump بنائیں، جیسا کہ docker compose exec -T db pg_dump -U postgres appdb > appdb.sql میں دکھایا گیا ہے۔ اس سے ایک plain file بنتی ہے، جسے آپ اپنی compose files کے ساتھ معمول کے encrypted restic backup routine میں شامل کر سکتے ہیں۔

نامزد volume پر bind mount منتقل کرنا

یہ منتقلی copy کے ذریعے ہوتی ہے، rename کے ذریعے نہیں، اور تقریباً ایک منٹ لیتی ہے۔

docker compose down
docker volume create myapp_pgdata
docker run --rm \
  -v "$PWD/data":/from \
  -v myapp_pgdata:/to \
  alpine sh -c 'cp -a /from/. /to/'

cp -a ownership، modes اور timestamps برقرار رکھتا ہے، اس لیے جو container user پرانی directory پڑھ سکتا تھا، وہ نئی volume بھی پڑھ سکتا ہے۔ اس کے بعد service کو pgdata:/var/lib/postgresql/data استعمال کرنے کے لیے تبدیل کریں، top-level volumes: block میں pgdata شامل کریں، docker compose up -d چلائیں، اور پرانی directory حذف کرنے سے پہلے application کے logs پڑھیں۔ دوسری سمت منتقلی کے لیے بھی یہی command استعمال کریں، لیکن /from اور /to کو باہم تبدیل کر دیں۔

test کے دوران ایک بات ذہن میں رکھیں۔ docker compose down named volumes کو برقرار رکھتا ہے، لیکن docker compose down -v project کی طرف سے declare کی گئی ہر named volume حذف کر دیتا ہے، اور اسے واپس کرنے کا کوئی طریقہ نہیں ہے۔ bind mount دونوں صورتوں میں برقرار رہتا ہے، کیونکہ Docker نے اس directory کی ملکیت کبھی نہیں لی۔ اگر lifecycle commands ابھی آپ کے لیے نئی ہیں تو VPS کے لیے Docker Compose basics guide ان کا طریقہ مرحلہ وار بیان کرتی ہے۔

سروس کے لحاظ سے انتخاب

یہ معلوم کریں کہ فائل کون لکھتا ہے۔ جس configuration کو آپ text editor میں edit کرکے git میں commit کرتے ہیں، اسے bind mount میں رکھیں اور :ro پر mount کریں، کیونکہ آپ چاہتے ہیں کہ وہ نظر آتی رہے اور اس کے versions محفوظ ہوں۔ Application state جسے آپ کبھی دستی طور پر نہیں کھولتے، اسے named volume میں رکھیں، کیونکہ Docker permissions کو image سے initialize کرتا ہے اور data کا انحصار host path پر نہیں رہتا۔

درمیانی صورت media کی ہے۔ Photo library application لکھتی ہے، لیکن اسے آپ بھی manage کرتے ہیں، اور یہ اکثر اتنی بڑی ہوتی ہے کہ اس کے لیے مخصوص disk درکار ہوتی ہے۔ اسے اس disk کے کسی path پر bind mount کریں اور ایک بار ownership واضح طور پر set کریں۔ یہی وہ pattern ہے جس پر زیادہ تر self-hosted stacks قائم ہوتے ہیں: databases اور caches کے لیے named volumes، configuration کے لیے bind mounts، اور اس بڑی directory کے لیے بھی bind mount جس کی آپ کو خاص ضرورت ہے۔ VPS پر چلنے والا Chatwoot اسی model میں آتا ہے: Postgres named volume میں ہوتا ہے، جبکہ uploaded attachments ایسے path پر ہوتے ہیں جسے آپ backup کے لیے مقرر کر سکتے ہیں۔

FAQ

bind mount اور named volume میں کیا فرق ہے؟

bind mount host کے کسی path کو container میں map کرتا ہے، اس لیے دونوں طرف ایک ہی directory نظر آتی ہے اور آپ اسے عام tools سے edit کر سکتے ہیں۔ named volume وہ storage ہے جسے Docker بناتا اور manage کرتا ہے، اور جس کا حوالہ نام سے دیا جاتا ہے۔ اسے top-level volumes: block میں declare کیا جاتا ہے۔ عملی فرق ownership کا ہے: اپنی maintain کی ہوئی configuration کے لیے bind mounts، اور application کے maintain کیے ہوئے data کے لیے named volumes استعمال کریں۔

bind mount کے ساتھ "permission denied" کیوں ملتا ہے، لیکن named volume کے ساتھ نہیں؟

خالی named volume کو image سے ابتدائی مواد ملتا ہے، اس لیے عموماً اسے image کی مقرر کردہ ownership وراثت میں مل جاتی ہے اور container user اس میں لکھ سکتا ہے۔ اس میں permissions پھر بھی نافذ رہتی ہیں، اس لیے اگر image میں اس path پر کچھ موجود نہ ہو تو یہ بھی اسی طرح ناکام ہو سکتا ہے۔ bind mount host directory کو بالکل اسی حالت میں دکھاتا ہے، اور اگر Docker کو وہ directory بنانی پڑی ہو تو اس نے اسے root کی ملکیت میں بنایا ہوگا۔ Container کے استعمال کردہ numeric id کو دیکھنے کے لیے docker compose exec <service> id چلائیں، پھر host directory کی ownership sudo chown -R <uid>:<gid> کے ذریعے تبدیل کریں، یا، جب daemon user namespaces کو remap نہ کرتا ہو، service پر user: "1000:1000" set کریں۔ اگر daemon user-namespace remapping استعمال کرتا ہے تو اس کے بجائے mapped host id کی ملکیت مقرر کریں، کیونکہ container کے ids host پر منتقل ہو جاتے ہیں اور user: اس تبدیلی کو نہیں روکتا۔

Docker named volumes کو disk پر کہاں محفوظ کرتا ہے؟

Default local driver کے ساتھ یہ /var/lib/docker/volumes/<volume>/_data کے تحت محفوظ ہوتے ہیں، اور docker volume inspect <volume> درست Mountpoint دکھاتا ہے۔ اگر کسی چیز کی جانچ کرنی ہو تو اسے پڑھیں، لیکن اس میں صرف container کے ذریعے لکھیں، کیونکہ host پر root کے طور پر editing کرنے سے ownership اس طرح بدل سکتی ہے جس کی container توقع نہیں کرتا۔

named volume کا backup کیسے لوں؟

ایک مختصر مدت کے لیے چلنے والا container شروع کریں، جس میں volume اور host directory دونوں mount ہوں، پھر docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data . کے ذریعے ایک جگہ سے دوسری جگہ archive بنائیں۔ Database کے لیے live files copy کرنے کے بجائے database کے اپنے tool سے dump بنائیں، کیونکہ writes جاری ہونے کے دوران بنائی گئی file copy corrupt state میں restore ہو سکتی ہے۔

کیا docker compose down میرے volumes delete کرتا ہے؟

docker compose down containers اور networks کو remove کرتا ہے اور named volumes کو برقرار رکھتا ہے۔ docker compose down -v project کے declare کیے گئے ہر named volume کو بھی delete کرتا ہے، اور یہ مستقل طور پر ہوتا ہے۔ دونوں commands bind mounts کو کبھی remove نہیں کرتیں، کیونکہ وہ directory host کی ملکیت ہوتی ہے، Docker کی نہیں۔

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