Docker Compose میں bind mount یا named volume؟
Docker Compose میں config کے لیے bind mount اور database data کے لیے named volume چنیں، permission traps سمجھیں، اور inspect، backup اور migration کا درست طریقہ جانیں۔
Bind mount یا named volume: مختصر جواب
Docker Compose volumes کی دو اقسام ہیں، اور انتخاب کا تعلق اس بات سے ہے کہ فائلوں کا مالک کون ہے۔ ان فائلوں کے لیے bind mount استعمال کریں جنہیں آپ خود لکھتے اور پڑھتے ہیں، مثلاً config، templates اور static sites۔ اس ڈیٹا کے لیے named volume استعمال کریں جس کا انتظام application کرتی ہے، مثلاً database files، search indexes اور uploaded media۔ 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 فائل میں دو نحوی طریقے
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 نامزد volume ہے۔ ./nginx.conf:/etc/nginx/nginx.conf bind mount ہے، اور :ro اسے صرف پڑھنے کے لیے mount کرتا ہے۔ یہ config کے لیے درست ڈیفالٹ ہے، کیونکہ container کو اسے کبھی دوبارہ لکھنے کی ضرورت نہیں ہونی چاہیے۔ اگر اوپری سطح کی volumes: اندراج بھول جائیں تو Compose service "db" refers to undefined volume pgdata کے ساتھ رک جاتا ہے۔
اسے شروع کریں اور دیکھیں کہ Docker نے کیا بنایا:
docker compose up -d
docker volume lsvolume کا نام pgdata نہیں ہے۔ اس کا نام <project>_pgdata ہے، جہاں project کا نام ڈیفالٹ طور پر اس directory کے نام کے مطابق ہوتا ہے جس میں compose فائل موجود ہو۔ myapp نام کی directory کے لیے نتیجہ myapp_pgdata ہوگا۔ یہ اس لیے اہم ہے کہ directory کا نام تبدیل کرنے سے نیا خالی volume بن جاتا ہے، اور application کو محسوس ہوتا ہے کہ اس کا ڈیٹا ضائع ہو گیا ہے۔ ایسا نہیں ہوا: پرانا volume اب بھی docker volume ls میں درج ہوتا ہے۔ compose فائل میں name: کے ذریعے نام مقرر کریں، یا COMPOSE_PROJECT_NAME متعین کریں، اگر 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 شروع ہو جاتا ہے۔
bind mount اس کے برعکس کام کرتا ہے۔ host پر جو کچھ موجود ہو، container کو وہی نظر آتا ہے، ownership سمیت، اور اس path پر موجود image کا مواد چھپ جاتا ہے۔ اگر host directory موجود نہ ہو تو Docker daemon اسے بناتا ہے۔ daemon root کے طور پر چلتا ہے، اس لیے directory root:root کی ملکیت میں بنتی ہے۔ اس کے بعد non-root user کے طور پر چلنے والا container process اس میں لکھ نہیں سکتا:
PermissionError: [Errno 13] Permission denied: '/data/app.db'حل یہ ہے کہ دونوں نمبروں کو یکساں کیا جائے۔ 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 کو اس نمبر کے مطابق کریں، یا service میں user: "1000:1000" کے ذریعے container کو اپنے نمبر پر مقرر کریں۔ اپنی لکھی ہوئی application کے لیے user: کو مقرر کرنا زیادہ صاف طریقہ ہے۔ ایسی image کے لیے جو آپ نے نہیں لکھی، host directory کی ownership تبدیل کرنا زیادہ محفوظ ہے، کیونکہ بعض images entrypoint کو root کے طور پر شروع کرتی ہیں، پھر privileges کم کرتی ہیں، اور اندر مخصوص ownership کی توقع رکھتی ہیں۔
دو مزید مسائل بھی اہم ہیں۔ Fedora، RHEL اور دیگر ایسے systems پر جہاں SELinux (security-enhanced Linux) enforcing ہو، bind mount کو relabel کیے جانے تک اجازت نہیں ملتی۔ اس لیے containers کے درمیان shared path کے لیے :z، یا صرف ایک container کے لیے مخصوص path کے لیے :Z شامل کریں، اور اسے - ./data:/data:Z کی صورت میں لکھیں۔ اسی طرح directory کے بجائے کسی ایک file کا bind mount بھی اس وقت ناکام ہو جاتا ہے جب editor file کو in-place لکھنے کے بجائے اسے replace کر دیتا ہے، کیونکہ mount اصل inode کے ساتھ منسلک رہتا ہے۔ container کو restart کرنے تک پرانا مواد ہی نظر آتا رہتا ہے۔ جس file میں بار بار ترمیم ہوتی ہو، اس کی parent directory mount کریں۔
کارکرد: فرق کہاں حقیقی ہے
Linux سرور پر دونوں اقسام ایک ہی kernel path سے گزرتی ہیں، اس لیے throughput کا فرق اتنا کم ہوتا ہے کہ آپ کو اس بنیاد پر انتخاب نہیں کرنا چاہیے۔ default local driver استعمال کرنے والے named volumes، Docker کے باقی ڈیٹا کی طرح /var/lib/docker/volumes/ کے تحت اسی filesystem پر رہتے ہیں، جبکہ bind mount وہاں رہتا ہے جہاں آپ نے اسے متعین کیا ہو۔
یہ فرق Docker Desktop for macOS اور Windows پر نمایاں ہوتا ہے، جہاں containers ایک virtual machine کے اندر چلتے ہیں۔ وہاں bind mount، file sharing layer کے ذریعے host filesystem سے اس 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 متعین کرتی ہیں۔
دوسرا حقیقی فرق یہ ہے کہ 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 تمام volumes کو ان کے size کے ساتھ دکھاتا ہے اور ان volumes کو نشان زد کرتا ہے جنہیں اب کوئی container استعمال نہیں کرتا۔
نامزد کردہ volume کا معائنہ
نامزد کردہ volume کوئی بند ڈبہ نہیں ہے۔ Docker سے اس کا مقام معلوم کریں:
docker volume inspect myapp_pgdataMountpoint فیلڈ میزبان کا حقیقی path فراہم کرتا ہے، جو عموماً /var/lib/docker/volumes/myapp_pgdata/_data ہوتا ہے۔ آپ اسے sudo ls کے ذریعے پڑھ سکتے ہیں، اور فوری جانچ کے لیے یہ مفید ہے۔ اسے فائلوں میں ترمیم کرنے کی جگہ نہ سمجھیں۔ وہاں root کے طور پر لکھنے سے اوپر بیان کردہ ملکیت کا مسئلہ دوبارہ پیدا ہو جاتا ہے، اور یہ path local driver کی ایک تفصیل ہے جسے دوسرے volume drivers شیئر نہیں کرتے۔
اندر دیکھنے کا محفوظ طریقہ یہ ہے کہ ایسا عارضی 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 اسے پہلے ہی سنبھال لیتا ہے۔ Backup کو host path پر مقرر کریں اور کام مکمل ہو جاتا ہے۔ Named volume کے لیے ایک اضافی مرحلہ درکار ہوتا ہے، کیونکہ tool کو اس کے اندر داخل ہونا ہوتا ہے۔ 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 بنتا ہے، اور یہ corrupt state میں restore ہو سکتا ہے۔ پہلے service کو روکیں، یا database کے اپنے tool کے ذریعے dump بنائیں، جیسا کہ docker compose exec -T db pg_dump -U postgres appdb > appdb.sql میں ہے۔ اس سے ایک plain file بنتی ہے، جسے آپ اپنی compose files کے ساتھ معمول کے encrypted restic backup routine میں شامل کر سکتے ہیں۔
bind mount کو named volume میں منتقل کرنا
یہ منتقلی 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 کو آپس میں بدل دیں۔
جانچ کے دوران ایک بات ذہن میں رکھیں۔ docker compose down named volumes کو برقرار رکھتا ہے، لیکن docker compose down -v project کی طرف سے declare کی گئی ہر named volume حذف کر دیتا ہے، اور اسے واپس نہیں کیا جا سکتا۔ bind mount دونوں commands کے بعد برقرار رہتا ہے، کیونکہ Docker اس directory کا مالک نہیں ہوتا۔ اگر lifecycle commands آپ کے لیے ابھی نئی ہیں تو VPS کے لیے Docker Compose کی بنیادی رہنما ان کا طریقہ بیان کرتی ہے۔
ہر سروس کے لیے انتخاب
یہ معلوم کریں کہ فائل کون لکھتا ہے۔ جس configuration میں آپ text editor کے ذریعے ترمیم کرتے ہیں اور اسے git میں commit کرتے ہیں، اسے bind mount میں رکھیں اور اسے :ro کے طور پر mount کریں، کیونکہ آپ چاہتے ہیں کہ وہ نظر آتی رہے اور versioned ہو۔ Application state جسے آپ کبھی دستی طور پر نہیں کھولتے، اسے named volume میں رکھیں، کیونکہ Docker permissions درست طور پر ترتیب دیتا ہے اور data کا انحصار host path پر نہیں ہوتا۔
درمیانی صورت media کی ہے۔ Photo library application لکھتی ہے، لیکن اس کا انتظام آپ بھی کرتے ہیں، اور یہ اکثر اتنی بڑی ہوتی ہے کہ اس کے لیے مخصوص disk درکار ہوتی ہے۔ اسے اس disk کے کسی path پر bind mount کریں، اور ایک بار اس کی ownership واضح طور پر مقرر کریں۔ زیادہ تر self-hosted stacks اسی طریقے پر قائم رہتے ہیں: databases اور caches کے لیے named volumes، configuration کے لیے bind mounts، اور اس بڑے directory کے لیے بھی bind mount جس کی آپ کو خاص ضرورت ہے۔
FAQ
bind mount اور named volume میں کیا فرق ہے؟
bind mount، host کے کسی path کو container میں map کرتا ہے، اس لیے دونوں جانب ایک ہی directory نظر آتی ہے اور آپ اسے عام tools سے edit کر سکتے ہیں۔ 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 کے ساتھ نہیں؟
ایک empty named volume، image سے seed ہوتا ہے۔ اس لیے اسے image کی مقرر کردہ ownership وراثت میں ملتی ہے اور container user اس میں write کر سکتا ہے۔ bind mount، host directory کو عین اسی حالت میں دکھاتا ہے۔ اگر Docker کو یہ directory بنانی پڑی ہو تو اس نے اسے root کی ownership میں بنایا ہوگا۔ container کے استعمال کردہ numeric id کو دیکھنے کے لیے docker compose exec <service> id چلائیں۔ پھر host directory پر sudo chown -R <uid>:<gid> چلائیں، یا service پر user: "1000:1000" set کریں۔
Docker named volumes کو disk پر کہاں store کرتا ہے؟
default local driver کے ساتھ یہ /var/lib/docker/volumes/<volume>/_data کے تحت رہتے ہیں، اور docker volume inspect <volume> درست Mountpoint دکھاتا ہے۔ اگر کسی چیز کی جانچ کرنی ہو تو اسے پڑھیں، لیکن اس میں صرف container کے ذریعے write کریں، کیونکہ host پر root کے طور پر edit کرنے سے 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 لیں، کیونکہ write operations کے دوران لی گئی file copy بحال ہونے پر corrupt حالت میں جا سکتی ہے۔
کیا docker compose down میرے volumes delete کرتا ہے؟
docker compose down containers اور networks remove کرتا ہے، لیکن named volumes برقرار رہتے ہیں۔ docker compose down -v project کے declare کردہ ہر named volume کو بھی delete کرتا ہے، اور یہ عمل مستقل ہوتا ہے۔ دونوں میں سے کوئی بھی command bind mounts کو remove نہیں کرتی، کیونکہ وہ directory host کی ملکیت ہے، Docker کی نہیں۔