SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-10-30

Docker Compose volume: bind mount নাকি named volume?

config ও database data-র জন্য bind mount বা named volume বাছুন। file permission সমস্যা এড়ান, volume inspect করুন এবং নিরাপদে backup ও migrate করার পদ্ধতি জানুন।

Bind mount নাকি named volume: সংক্ষিপ্ত উত্তর

Docker Compose volume দুই ধরনের। পছন্দটি নির্ভর করে ফাইলের মালিকানা কার ওপর থাকবে তার ওপর। আপনি নিজে যে ফাইল লিখবেন ও পড়বেন, যেমন config, template এবং static site, সেগুলোর জন্য bind mount ব্যবহার করুন। যে data application নিজে পরিচালনা করবে, যেমন database file, search index এবং uploaded media, সেগুলোর জন্য named volume ব্যবহার করুন। bind mount host-এর এমন একটি path নির্দেশ করে, যা আপনি editor-এ খুলতে পারেন। named volume হলো Docker-এর তৈরি ও পরিচালিত storage, যেটিতে Docker-এর মাধ্যমেই পৌঁছাতে হয়।

একটি service-এর মধ্যে উভয় ধরনের volume একই volumes: key-এর অধীনে থাকে। তাই এগুলো প্রায়ই গুলিয়ে যায়। পার্থক্যটি colon-এর বাম পাশে। . বা / দিয়ে শুরু হওয়া বাম পাশের অংশ host path নির্দেশ করে, তাই সেটি bind mount। অন্য যেকোনো অংশ একটি name, তাই সেটি named volume; এবং সেই name-টি top-level volumes: block-এও ঘোষণা করতে হবে।

Compose file-এ দুটি 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 করে। কোনো container যে configuration কখনো পুনর্লিখবে না, তার জন্য এটিই সঠিক default। 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 হলো compose file থাকা directory-এর নাম। myapp নামের একটি directory হলে নামটি হবে myapp_pgdata। এটি গুরুত্বপূর্ণ, কারণ directory-এর নাম পরিবর্তন করলে আপনি একটি নতুন empty volume পান এবং application-টি মনে হয় তার data হারিয়েছে। আসলে তা হয়নি: পুরোনো volume এখনও docker volume ls-এর তালিকায় আছে। Directory সরতে পারে এমন ক্ষেত্রে compose file-এ name: দিয়ে নাম নির্দিষ্ট করুন, অথবা COMPOSE_PROJECT_NAME সেট করুন। এ ধরনের setting আপনার অন্যান্য Compose environment file ও secret-এর সঙ্গে রাখা উচিত।

কেন permission error মূলত bind mount-এ বেশি দেখা যায়

এটাই সবচেয়ে গুরুত্বপূর্ণ ব্যবহারিক পার্থক্য। এর মূল কারণ একটি নিয়ম: প্রথমবার ব্যবহারের সময় কোনো named volume খালি থাকলে সেটি image থেকে seed হয়, কিন্তু bind mount কখনোই তা হয় না।

Docker যখন image-এ আগে থেকেই থাকা content-সমৃদ্ধ কোনো directory-এর ওপর খালি named volume mount করে, তখন image নির্ধারিত ownership ও mode বজায় রেখে সেই content volume-এ কপি করে। Official Postgres image-এ /var/lib/postgresql/data তার নিজস্ব postgres user-এর মালিকানাধীন থাকে। তাই volume-টিও একই numeric id-এর মালিকানাধীন হয় এবং database চালু হয়।

এই seeding-ই named volume-এর একমাত্র অতিরিক্ত সুবিধা। Docker volume-টি কোথায় থাকবে তা পরিচালনা করে, কিন্তু container সাধারণ filesystem operation ব্যবহার করে volume-এ read ও write করে। তাই bind mount-এর মতো এখানেও ownership ও mode কার্যকর থাকে। Image-এ নেই এমন কোনো path-এ mounted named volume, অথবা অন্য image বা user দিয়ে প্রথমে পূরণ করা volume, image থেকে কোনো ownership পায় না। সেখানেও non-root process-এর access প্রত্যাখ্যাত হতে পারে। নিচের id check উভয় ধরনের mount-এর ক্ষেত্রেই কাজ করে।

Bind mount ঠিক বিপরীতভাবে কাজ করে। Host-এ যা আছে, ownership-সহ container-ও সেটিই দেখে, এবং ওই path-এ থাকা image-এর content আড়াল হয়ে যায়। Host directory না থাকলে Docker daemon সেটি তৈরি করে। Daemon root হিসেবে চলে, তাই directory-টির মালিক হয় root:root। এরপর non-root user হিসেবে চলা container process সেখানে লিখতে পারে না:

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

সমাধান হলো numeric id মিলিয়ে দেওয়া। Bind mount-এ ownership name দিয়ে নয়, 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-এ নির্ধারণ করা হয়)। Remapping প্রতিটি container id-কে host-এর একটি subordinate range-এ স্থানান্তর করে। 231072 থেকে শুরু হওয়া Docker-এর উদাহরণ 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 container process বাস্তবে কোন uid হিসেবে চলছে তা দেখায়। Remapping না থাকলে host directory-টির ownership ওই সংখ্যার সঙ্গে মিলিয়ে দিন, অথবা service-এ user: "1000:1000" ব্যবহার করে container-টিকে আপনার uid-এ চালান। Remapping চালু থাকলে user: দিয়ে container-কে আপনার host number-এ pin করা যায় না, কারণ নির্ধারিত id-টিও স্থানান্তরিত হয়। ওই উদাহরণ range-এ user: "1000:1000"-ও host uid 232072-এ গিয়ে পৌঁছায়। পরিবর্তে host directory-টির ownership mapped host id-এ পরিবর্তন করুন। আপনার নিজের range /etc/subuid এবং /etc/subgid-এ আছে। Docker-এর documentation অনুযায়ী একক container-এর জন্য remapping বন্ধ করার পদ্ধতি হলো docker container create, run বা exec-এ শুধু --userns=host নির্ধারণ করা। নিজের লেখা application-এর জন্য user: pin করা বেশি পরিষ্কার পদ্ধতি। নিজে না লেখা image-এর ক্ষেত্রে host directory-র ownership পরিবর্তন করা নিরাপদ, কারণ কিছু image root হিসেবে entrypoint শুরু করে, পরে privilege কমায়, এবং নির্দিষ্ট ownership আশা করে।

আরও দুটি বিষয় জানা দরকার। Fedora, RHEL এবং SELinux (security-enhanced Linux) enforcing থাকা অন্যান্য system-এ relabel না করা পর্যন্ত bind mount প্রত্যাখ্যাত হয়। তাই একাধিক container-এর মধ্যে shared path-এর জন্য :z, অথবা কেবল একটি container ব্যবহারের path-এর জন্য :Z যোগ করুন, অর্থাৎ - ./data:/data:Z হিসেবে লিখুন। আর directory-এর বদলে একটি single file bind mount করলে editor file-টি in-place লেখার পরিবর্তে নতুন file দিয়ে প্রতিস্থাপন করলে সমস্যা হয়, কারণ mount-টি original inode অনুসরণ করে। Container restart না করা পর্যন্ত পুরোনো content-ই দেখতে থাকে। File ঘন ঘন edit হলে parent directory mount করুন।

Performance: যেখানে পার্থক্য বাস্তব

Linux server-এ উভয় ধরনের storage একই kernel path ব্যবহার করে। তাই throughput-এর পার্থক্য এত কম যে এর ভিত্তিতে সিদ্ধান্ত নেওয়া উচিত নয়। ডিফল্ট local driver ব্যবহার করা named volume-গুলো Docker-এর বাকি data-এর মতো একই filesystem-এ, /var/lib/docker/volumes/-এর অধীনে থাকে। bind mount থাকে আপনি যে location নির্ধারণ করেছেন সেখানে।

Docker Desktop for macOS এবং Windows-এ এই পার্থক্য দেখা যায়, কারণ container-গুলো একটি virtual machine-এর ভিতরে চলে। সেখানে bind mount host filesystem থেকে file sharing layer-এর মাধ্যমে ওই virtual machine-এ যায়। Node.js dependency tree বা PHP framework cache-এর মতো অনেক ছোট file operation থাকা workload-এ এর ফলে লক্ষণীয়ভাবে গতি কমে। named volume virtual machine-এর ভিতরেই থাকে এবং এই অতিরিক্ত খরচ হয় না। এ কারণেই development compose file-এ source directory bind-mount করা হয়, কিন্তু node_modules-এর ওপর একটি named volume নির্ধারণ করা হয়।

আরেকটি বাস্তব পার্থক্য হলো data কোথায় লেখা হয়। /mnt/backup-এ bind mount করলে data ওই disk-এ লেখা হয়। named volume-এর data /var/lib/docker ধারণকারী filesystem-এ লেখা হয়, যা VPS-এ সাধারণত root disk। named volume-এর ভিতরে বাড়তে থাকা database সেই একই disk ভরে দেয়, যেখানে system log-ও থাকে। সমস্যা তৈরি হওয়ার আগে এটি পরীক্ষা করুন:

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

docker system df -v size-সহ প্রতিটি volume তালিকাভুক্ত করে এবং যেসব volume আর কোনো container ব্যবহার করে না, সেগুলো চিহ্নিত করে।

নামযুক্ত volume পরীক্ষা করা

নামযুক্ত volume কোনো black box নয়। Docker-কে এর অবস্থান জানাতে বলুন:

docker volume inspect myapp_pgdata

Mountpoint field-এ host-এর প্রকৃত path দেখা যায়, সাধারণত /var/lib/docker/volumes/myapp_pgdata/_data। sudo ls দিয়ে এটি পড়তে পারেন। দ্রুত পরীক্ষা করার জন্য এটি উপযোগী। তবে এই path-কে file edit করার স্থান হিসেবে ব্যবহার করবেন না। সেখানে root হিসেবে write করলে উপরে বর্ণিত ownership সমস্যা আবার তৈরি হবে। তাছাড়া path-টি local driver-এর একটি নির্দিষ্ট বিবরণ, যা অন্য volume driver-এ নাও থাকতে পারে।

ভেতরের বিষয়বস্তু দেখার নিরাপদ উপায় হলো volume mount করে একটি সাময়িক container চালানো:

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

যেকোনো driver-এর ক্ষেত্রে এটি কাজ করে। প্রকৃত container যে permissions দেখে, এটিও একই permissions দেখে। --rm-এর কারণে কাজ শেষে কোনো কিছু অবশিষ্ট থাকে না।

প্রতিটি ধরনের ব্যাকআপ নেওয়া

Bind mount একটি সাধারণ directory, তাই যেকোনো file-level backup tool এটি স্বয়ংক্রিয়ভাবে পরিচালনা করতে পারে। Backup-এর জন্য host path নির্দিষ্ট করলেই কাজ শেষ। Named volume-এর ক্ষেত্রে আরও একটি ধাপ প্রয়োজন, কারণ tool-টিকে volume-এর ভেতরে প্রবেশ করতে হয়। একই short-lived container-এ volume এবং একটি host directory mount করুন, তারপর একটি archive তৈরি করুন:

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

নতুন volume-এ উল্টোভাবে restore করুন:

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

Container-এর ভেতরে root হিসেবে চলার সময় tar numeric ownership সংরক্ষণ করে। এটাই restored volume-কে application-এর জন্য ব্যবহারযোগ্য রাখে।

উভয় ধরনের ক্ষেত্রেই একটি সতর্কতা প্রযোজ্য। Database চলমান অবস্থায় তার file কপি করলে এমন একটি archive তৈরি হয়, যা পরিবর্তনশীল অবস্থার snapshot। এটি restore করার পরে corrupt state তৈরি হতে পারে। আগে service বন্ধ করুন, অথবা database-এর নিজস্ব tool ব্যবহার করে dump নিন, যেমন docker compose exec -T db pg_dump -U postgres appdb > appdb.sql-এ দেখানো হয়েছে। এতে একটি সাধারণ file তৈরি হয়। এরপর compose file-এর পাশাপাশি সেটিকে স্বাভাবিক encrypted restic backup routine-এ অন্তর্ভুক্ত করতে পারেন।

named volume-এ bind mount স্থানান্তর

এই স্থানান্তরটি rename নয়, copy operation। এটি সম্পন্ন হতে প্রায় এক মিনিট লাগে।

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, mode এবং timestamp অপরিবর্তিত রাখে। তাই পুরোনো directory পড়তে পারা container user নতুন volume-টিও পড়তে পারে। এরপর service-টি pgdata:/var/lib/postgresql/data ব্যবহার করার জন্য পরিবর্তন করুন, top-level volumes: block-এ pgdata যোগ করুন, docker compose up -d চালান এবং পুরোনো directory মুছে ফেলার আগে application-এর log পরীক্ষা করুন। উল্টো দিকে যেতে হলে একই command ব্যবহার করুন, তবে /from এবং /to অদলবদল করুন।

পরীক্ষা করার সময় একটি বিষয় মনে রাখুন। docker compose down named volume অপরিবর্তিত রাখে। কিন্তু docker compose down -v project-এ ঘোষিত প্রতিটি named volume মুছে দেয়, এবং এটি undo করা যায় না। bind mount উভয় ক্ষেত্রেই টিকে থাকে, কারণ Docker কখনও ওই directory-টির মালিক ছিল না। lifecycle command-গুলো এখনও নতুন হলে VPS-এর জন্য Docker Compose-এর মৌলিক নির্দেশিকা-তে সেগুলোর ব্যবহার ধাপে ধাপে দেখানো হয়েছে।

service অনুযায়ী নির্বাচন

ফাইলটি কে লেখে, তা নির্ধারণ করুন। কোনো text editor-এ সম্পাদনা করে git-এ commit করা configuration একটি bind mount-এ রাখুন, যা :ro-এ mount করা হয়েছে। কারণ এটি আপনার কাছে দৃশ্যমান এবং versioned থাকা দরকার। যে application state আপনি হাতে কখনো খোলেন না, সেটি একটি named volume-এ রাখুন। এতে Docker image থেকে permissions নির্ধারণ করে, এবং data কোনো host path-এর ওপর নির্ভর করে না।

মিশ্র পরিস্থিতির উদাহরণ হলো media। Photo library application লিখে, কিন্তু আপনিও এটি পরিচালনা করেন। এটি প্রায়ই এত বড় হয় যে এর জন্য নির্দিষ্ট disk ব্যবহার করতে চান। এটিকে ওই disk-এর একটি path-এ bind mount করুন এবং একবার ইচ্ছাকৃতভাবে ownership নির্ধারণ করুন। অধিকাংশ self-hosted stack শেষ পর্যন্ত এই পদ্ধতিই ব্যবহার করে: database ও cache-এর জন্য named volume, configuration-এর জন্য bind mount, এবং আপনার কাছে গুরুত্বপূর্ণ বড় directory-টির জন্য bind mount। VPS-এ চলা Chatwoot-এর মতো একটি support desk ঠিক এই বিন্যাসে থাকে: Postgres একটি named volume-এ এবং uploaded attachment এমন একটি path-এ থাকে, যেটি backup-এর জন্য নির্দিষ্ট করে দিতে পারেন।

FAQ

Bind mount এবং named volume-এর মধ্যে পার্থক্য কী?

Bind mount host-এর একটি path container-এর মধ্যে map করে। তাই উভয় পাশে একই directory দেখা যায় এবং সাধারণ tool দিয়ে সেটি edit করা যায়। Named volume হলো Docker তৈরি ও পরিচালিত storage, যেটিকে name দিয়ে উল্লেখ করা হয় এবং top-level volumes: block-এ ঘোষণা করা হয়। ব্যবহারিক পার্থক্য ownership-এ: আপনি নিজে পরিচালনা করেন এমন config-এর জন্য bind mount, আর application নিজে পরিচালনা করে এমন data-এর জন্য named volume ব্যবহার করুন।

Bind mount-এ "permission denied" পাই, কিন্তু named volume-এ পাই না কেন?

খালি named volume image থেকে seed হয়। তাই সাধারণত image নির্ধারিত ownership সেটিতে চলে আসে এবং container user সেখানে লিখতে পারে। তবু volume-এ permission কার্যকর থাকে। ওই path-এ image-এর কিছু না থাকলে একই কারণে volume-ও ব্যর্থ হতে পারে। Bind mount host directory-কে ঠিক যেমন আছে তেমনই দেখায়। Docker-কে directory তৈরি করতে হলে সেটি root-এর মালিকানাধীন হয়। Container কোন numeric id ব্যবহার করছে তা দেখতে docker compose exec <service> id চালান। এরপর host directory-তে sudo chown -R <uid>:<gid> চালান। অথবা daemon user namespace remap না করলে service-এ user: "1000:1000" নির্ধারণ করুন। Daemon user-namespace remapping ব্যবহার করলে mapped host id-এ chown করুন, কারণ container-এর id host-এ স্থানান্তরিত হয় এবং user: তা পরিবর্তন করে না।

Docker named volume disk-এ কোথায় সংরক্ষণ করে?

Default local driver ব্যবহার করলে volume-গুলো /var/lib/docker/volumes/<volume>/_data-এর অধীনে থাকে। সঠিক Mountpoint দেখতে docker volume inspect <volume> চালান। কিছু পরীক্ষা করতে হলে এটি পড়তে পারেন, কিন্তু host থেকে সেখানে লিখবেন না। কারণ root হিসেবে edit করলে ownership এমনভাবে পরিবর্তিত হতে পারে, যা container প্রত্যাশা করে না। Volume-এ লিখতে হলে container-এর মাধ্যমেই লিখুন।

Named volume-এর backup কীভাবে নেব?

Volume এবং host directory দুটিই mount করে একটি short-lived container চালান। এরপর docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data . ব্যবহার করে এক স্থান থেকে অন্য স্থানে archive তৈরি করুন। Database-এর ক্ষেত্রে live file কপি না করে database-এর নিজস্ব tool দিয়ে dump নিন। কারণ write চলার সময় নেওয়া file copy restore করলে database corrupt অবস্থায় ফিরতে পারে।

docker compose down কি আমার volume মুছে দেয়?

docker compose down container ও network সরিয়ে দেয়, কিন্তু named volume রেখে যায়। docker compose down -v project-এ ঘোষিত প্রতিটি named volume-ও মুছে দেয়, এবং তা স্থায়ীভাবে মুছে যায়। কোনো command-ই bind mount সরায় না, কারণ ওই directory host-এর মালিকানাধীন, Docker-এর নয়।

এই লেখাটি কি কাজে লেগেছে?