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 lsvolume کا نام 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 پر permission errors ہی کیوں اثر انداز ہوتے ہیں
یہ سب سے اہم عملی فرق ہے، اور اس کی وجہ ایک اصول ہے: پہلی بار استعمال کے وقت خالی named volume کو image سے ابتدائی مواد ملتا ہے، جبکہ bind mount کو نہیں ملتا۔
جب Docker کسی خالی named volume کو image میں پہلے سے موجود مواد والی directory پر mount کرتا ہے، تو وہ مواد image میں مقررہ ownership اور modes کے ساتھ volume میں copy ہو جاتا ہے۔ Official Postgres image میں /var/lib/postgresql/data اس کے اپنے postgres user کی ملکیت ہوتا ہے۔ اس لیے volume بھی اسی numeric id کی ملکیت میں آتا ہے اور database start ہو جاتا ہے۔
bind mount اس کے برعکس کام کرتا ہے۔ Host پر جو کچھ موجود ہو، container اسی کو ownership سمیت دیکھتا ہے، اور اس path پر موجود image کا مواد پوشیدہ ہو جاتا ہے۔ اگر host directory موجود نہ ہو تو Docker daemon اسے بناتا ہے، اور daemon root کے طور پر چلتا ہے، اس لیے آپ کو root:root کی ملکیت والی directory ملتی ہے۔ اس کے بعد non-root user کے طور پر چلنے والا container process اس میں write نہیں کر سکتا:
PermissionError: [Errno 13] Permission denied: '/data/app.db'حل یہ ہے کہ دونوں طرف کے numbers یکساں ہوں۔ bind mount میں ownership کا موازنہ numeric user id سے ہوتا ہے، نام سے نہیں، کیونکہ container کا اپنا /etc/passwd ہوتا ہے۔ container کے اندر app نام کا user ہونا host پر کوئی معنی نہیں رکھتا۔ Uid 1000 دونوں طرف uid 1000 ہی ہوتا ہے۔
id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./datadocker compose exec web id وہ uid دکھاتا ہے جس کے طور پر container process حقیقت میں چل رہا ہوتا ہے۔ Host directory کو اسی number کی ملکیت میں دیں، یا service میں user: "1000:1000" کے ذریعے container کو اپنے number پر مقرر کریں۔ اپنی لکھی ہوئی application کے لیے user: کو مقرر کرنا زیادہ صاف حل ہے۔ ایسی image کے لیے جسے آپ نے نہیں لکھا، host directory کی ملکیت تبدیل کرنا زیادہ محفوظ ہے، کیونکہ بعض images entrypoint کو root کے طور پر شروع کرتی ہیں، پھر privileges کم کرتی ہیں، اور اندر مخصوص ownership کی توقع رکھتی ہیں۔
دو مزید مسائل جاننا ضروری ہیں۔ Fedora، RHEL اور SELinux (security-enhanced Linux) کو enforcing حالت میں استعمال کرنے والے دیگر systems پر bind mount اس وقت تک denied رہتا ہے جب تک اس پر relabel نہ کیا جائے۔ اس لیے containers کے درمیان shared path کے لیے :z، یا صرف ایک container کے لیے مخصوص path کے لیے :Z شامل کریں، اور اسے - ./data:/data:Z کی صورت میں لکھیں۔ مزید یہ کہ directory کے بجائے کسی ایک file کا bind mount اس وقت ناکام ہو جاتا ہے جب editor file کو اسی جگہ لکھنے کے بجائے نئی file سے replace کر دے، کیونکہ mount اصل inode کی پیروی کرتا ہے۔ Container restart ہونے تک پرانا content دیکھتا رہتا ہے۔ اگر file میں بار بار ترمیم ہوتی ہو تو 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/dockerdocker system df -v ہر volume کو اس کے size کے ساتھ فہرست کرتا ہے، اور ان volumes کو نشان زد کرتا ہے جنہیں اب کوئی container استعمال نہیں کرتا۔
نام زدہ volume کا معائنہ
نام زدہ volume کوئی بند ڈبہ نہیں ہے۔ Docker سے معلوم کریں کہ یہ کہاں موجود ہے:
docker volume inspect myapp_pgdataMountpoint فیلڈ میزبان کا حقیقی 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 سے ترمیم کرتے ہیں اور اسے git میں commit کرتے ہیں، اسے bind mount میں رکھیں اور :ro پر mount کریں، کیونکہ آپ چاہتے ہیں کہ وہ نمایاں اور versioned رہے۔ Application state جسے آپ کبھی دستی طور پر نہیں کھولتے، اسے named volume میں رکھیں، کیونکہ Docker permissions درست طور پر set کرتا ہے اور data کا انحصار host path پر نہیں رہتا۔
درمیانی صورت media کی ہے۔ Photo library application کے ذریعے لکھی جاتی ہے، لیکن اسے آپ manage بھی کرتے ہیں، اور یہ اکثر اتنی بڑی ہوتی ہے کہ اس کے لیے مخصوص disk درکار ہو۔ اسے اس disk کے کسی path پر bind mount کریں اور ownership ایک مرتبہ واضح طور پر set کریں۔ زیادہ تر self-hosted stacks میں یہی pattern اختیار کیا جاتا ہے: databases اور caches کے لیے named volumes، configuration کے لیے bind mounts، اور اس بڑی directory کے لیے بھی bind mount جس کی آپ کو خاص ضرورت ہے۔ Support desk، جیسے VPS پر چلنے والا Chatwoot، عین اسی صورت میں آتا ہے: Postgres named volume میں، اور uploaded attachments ایسے path پر جسے آپ backup کے لیے متعین کر سکتے ہیں۔
FAQ
bind mount اور named volume میں کیا فرق ہے؟
bind mount، host کے کسی path کو container کے اندر map کرتا ہے، اس لیے دونوں طرف ایک ہی directory نظر آتی ہے اور آپ عام tools سے اس میں ترمیم کر سکتے ہیں۔ named volume وہ storage ہے جسے Docker خود بناتا اور manage کرتا ہے، اور جسے نام سے refer کیا جاتا ہے؛ اسے top-level volumes: block میں declare کیا جاتا ہے۔ عملی فرق ownership کا ہے: ایسی configuration کے لیے bind mounts استعمال کریں جسے آپ خود maintain کرتے ہیں، اور اس data کے لیے named volumes استعمال کریں جسے application خود maintain کرتی ہے۔
bind mount کے ساتھ "permission denied" کیوں ملتا ہے، لیکن named volume کے ساتھ نہیں؟
خالی named volume، image سے seed ہوتا ہے، اس لیے اسے image کی مقرر کردہ ownership ملتی ہے اور container user اس میں write کر سکتا ہے۔ bind mount، host directory کو بالکل اسی حالت میں دکھاتا ہے، اور اگر Docker کو یہ directory خود بنانی پڑی ہو تو اس کی ownership root کو دے دی جاتی ہے۔ container کے استعمال کردہ numeric id کو دیکھنے کے لیے docker compose exec <service> id چلائیں، پھر host directory پر sudo chown -R <uid>:<gid> چلائیں، یا service پر user: "1000:1000" مقرر کریں۔
Docker named volumes کو disk پر کہاں محفوظ کرتا ہے؟
default local driver کے ساتھ یہ /var/lib/docker/volumes/<volume>/_data کے تحت محفوظ ہوتے ہیں، اور docker volume inspect <volume> سے درست Mountpoint دکھتا ہے۔ اگر کسی چیز کی جانچ کرنی ہو تو اسے پڑھیں، لیکن اس میں صرف container کے ذریعے write کریں، کیونکہ host پر root کے طور پر ترمیم کرنے سے ownership اس طرح بدل سکتی ہے جس کی container توقع نہیں کرتا۔
named volume کا backup کیسے لوں؟
volume اور host directory، دونوں کو mount کر کے ایک مختصر مدت کے لیے container چلائیں، پھر 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 پیدا کر سکتی ہے۔
کیا docker compose down میرے volumes حذف کر دیتا ہے؟
docker compose down containers اور networks کو ہٹاتا ہے اور named volumes کو برقرار رکھتا ہے۔ docker compose down -v project کے declare کیے گئے تمام named volumes بھی حذف کر دیتا ہے، اور یہ عمل مستقل ہوتا ہے۔ دونوں commands bind mounts کو کبھی حذف نہیں کرتیں، کیونکہ وہ directory host کی ملکیت ہوتی ہے، Docker کی نہیں۔