SSD Nodes Learn 8GB RAM — $66/বছর
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-01

Docker Compose: Bind mount বনাম Named volume পার্থক্য

Docker Compose-এ Bind mount এবং Named volume ব্যবহারের সঠিক নিয়ম জানুন। কনফিগারেশন ও ডাটাবেস ডেটার জন্য কোনটি সেরা, ফাইল পারমিশন সমস্যা এবং ব্যাকআপ নেওয়ার পদ্ধতি এখানে দেখুন।

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

Docker Compose ভলিউম দুই ধরনের হয় এবং ফাইলগুলোর মালিকানা কার, তার ওপর ভিত্তি করে এদের বেছে নিতে হয়। আপনি নিজে যেসব ফাইল পড়েন বা লেখেন, যেমন কনফিগারেশন, টেমপ্লেট বা স্ট্যাটিক সাইটের জন্য bind mount ব্যবহার করুন। অ্যাপ্লিকেশন যে ডেটার মালিক, যেমন ডাটাবেস ফাইল, সার্চ ইনডেক্স বা আপলোড করা মিডিয়ার জন্য named volume ব্যবহার করুন। একটি bind mount হোস্টের এমন একটি পাথের দিকে নির্দেশ করে যা আপনি যেকোনো এডিটরে খুলতে পারেন। একটি named volume হলো এমন স্টোরেজ যা Docker আপনার জন্য তৈরি ও ট্র্যাক করে এবং আপনি Docker-এর মাধ্যমেই সেখানে পৌঁছাতে পারেন।

উভয়ই একটি সার্ভিসের ভেতরে একই volumes: কী-এর অধীনে থাকে, যার ফলে এদের মধ্যে বিভ্রান্তি তৈরি হয়। এদের পার্থক্য হলো কোলনের বাম দিকের অংশে। যদি বাম দিকটি . বা / দিয়ে শুরু হয়, তবে সেটি একটি হোস্ট পাথ, অর্থাৎ এটি একটি bind mount। অন্য যেকোনো কিছু একটি নাম হিসেবে গণ্য হয়, অর্থাৎ এটি একটি named volume এবং সেই নামটি অবশ্যই টপ-লেভেল volumes: ব্লকে ঘোষণা করতে হবে।

কম্পোজ ফাইলে দুটি সিনট্যাক্স

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 হলো একটি নেমড ভলিউম। ./nginx.conf:/etc/nginx/nginx.conf হলো একটি বাইন্ড মাউন্ট এবং :ro এটিকে রিড-অনলি হিসেবে মাউন্ট করে, যা কনফিগারেশনের জন্য সঠিক ডিফল্ট সেটিংস, কারণ কন্টেইনারের এটি পুনরায় লেখার প্রয়োজন নেই। টপ-লেভেল volumes: এন্ট্রিটি বাদ দিলে কম্পোজ service "db" refers to undefined volume pgdata এর মাধ্যমে কাজ বন্ধ করে দেয়।

এটি চালু করুন এবং ডকার কী তৈরি করেছে তা তালিকাভুক্ত করুন:

docker compose up -d
docker volume ls

ভলিউমটির নাম pgdata নয়। এর নাম <project>_pgdata, যেখানে প্রজেক্টের নাম ডিফল্টভাবে সেই ডিরেক্টরির নাম হয় যেখানে কম্পোজ ফাইলটি থাকে। myapp নামের একটি ডিরেক্টরি myapp_pgdata তৈরি করে। এটি গুরুত্বপূর্ণ কারণ ডিরেক্টরির নাম পরিবর্তন করলে আপনি একটি নতুন খালি ভলিউম পাবেন এবং মনে হবে অ্যাপ্লিকেশনটি তার ডেটা হারিয়ে ফেলেছে। আসলে তা নয়: পুরনো ভলিউমটি এখনও docker volume ls এর তালিকায় দেখা যায়। কম্পোজ ফাইলে name: দিয়ে নাম নির্দিষ্ট করুন অথবা ডিরেক্টরি স্থানান্তরিত হওয়ার সম্ভাবনা থাকলে COMPOSE_PROJECT_NAME সেট করুন। এই ধরনের সেটিংস আপনার অন্যান্য কম্পোজ এনভায়রনমেন্ট ফাইল এবং সিক্রেটস-এর সাথে রাখা উচিত।

কেন পারমিশন ত্রুটি শুধুমাত্র bind mount-এর ক্ষেত্রেই ঘটে

এটিই সবচেয়ে বড় ব্যবহারিক পার্থক্য, এবং এটি একটি নিয়ম থেকে আসে: প্রথম ব্যবহারের সময় একটি খালি named volume ইমেজ থেকে সিড (seed) করা হয়, কিন্তু একটি bind mount কখনোই তা হয় না।

যখন Docker একটি খালি named volume-কে এমন একটি ডিরেক্টরির ওপর মাউন্ট করে যেখানে ইমেজে আগে থেকেই কন্টেন্ট থাকে, তখন এটি সেই কন্টেন্টগুলোকে ভলিউমের ভেতর কপি করে নেয়, এবং ইমেজে সেট করা মালিকানা (ownership) ও মোডগুলো বজায় রাখে। অফিসিয়াল Postgres ইমেজটি /var/lib/postgresql/data নিয়ে আসে যা এর নিজস্ব postgres ব্যবহারকারীর মালিকানাধীন, তাই ভলিউমটিও সেই একই সংখ্যাসূচক আইডির মালিকানাধীন হয় এবং ডাটাবেসটি চালু হয়।

একটি bind mount ঠিক এর বিপরীত কাজ করে। হোস্টে যা কিছু থাকে, কন্টেইনার ঠিক তাই দেখতে পায়, যার মধ্যে মালিকানাও অন্তর্ভুক্ত, এবং সেই পাথে থাকা ইমেজের কন্টেন্টগুলো আড়াল হয়ে যায়। যদি হোস্ট ডিরেক্টরিটি বিদ্যমান না থাকে, তবে Docker daemon সেটি তৈরি করে, এবং daemon যেহেতু root হিসেবে চলে, তাই আপনি এমন একটি ডিরেক্টরি পাবেন যার মালিকানা root:root-এর। এরপর একটি নন-রুট ব্যবহারকারী হিসেবে চলমান কন্টেইনার প্রসেস সেখানে লিখতে পারে না:

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

এর সমাধান হলো সংখ্যাগুলোকে সামঞ্জস্যপূর্ণ করা। একটি bind mount-এর ক্ষেত্রে মালিকানা সংখ্যাসূচক ইউজার আইডি (uid) দ্বারা তুলনা করা হয়, নাম দ্বারা নয়, কারণ কন্টেইনারের নিজস্ব /etc/passwd থাকে। কন্টেইনারের ভেতরে app নামের একজন ব্যবহারকারীর হোস্টের কাছে কোনো অর্থ নেই। Uid 1000 মানে উভয় পাশেই uid 1000।

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

docker compose exec web id কমান্ডটি সেই uid প্রিন্ট করে যা দিয়ে কন্টেইনার প্রসেসটি প্রকৃতপক্ষে চলে। হোস্ট ডিরেক্টরিটিকে সেই সংখ্যার সাথে মিলিয়ে নিন, অথবা সার্ভিসে user: "1000:1000" ব্যবহার করে কন্টেইনারটিকে আপনার নির্দিষ্ট নম্বরে পিন করুন। আপনি নিজে লিখেছেন এমন অ্যাপ্লিকেশনের জন্য user: পিন করা বেশি পরিচ্ছন্ন। আপনি তৈরি করেননি এমন ইমেজের ক্ষেত্রে হোস্ট ডিরেক্টরিটিকে chown করা বেশি নিরাপদ, কারণ কিছু ইমেজ root হিসেবে এন্ট্রি পয়েন্ট শুরু করে, প্রিভিলেজ কমিয়ে দেয় এবং নির্দিষ্ট মালিকানা আশা করে।

আরও দুটি ফাঁদ সম্পর্কে জানা জরুরি। Fedora, RHEL এবং SELinux (security-enhanced Linux) এনফোর্সিং সিস্টেমগুলোতে, একটি bind mount রিল্যাবেল (relabel) না করা পর্যন্ত তা প্রত্যাখ্যান করা হয়, তাই কন্টেইনারগুলোর মধ্যে শেয়ার করা পাথের জন্য :z যোগ করুন অথবা শুধুমাত্র একটি কন্টেইনার ব্যবহার করবে এমন পাথের জন্য :Z যোগ করুন, যা - ./data:/data:Z হিসেবে লেখা হয়। এছাড়া, ডিরেক্টরির পরিবর্তে একটি একক ফাইল bind mount করলে তা ভেঙে যায় যখন কোনো এডিটর ফাইলটিকে সরাসরি না লিখে প্রতিস্থাপন (replace) করে, কারণ মাউন্টটি মূল inode অনুসরণ করে। আপনি রিস্টার্ট না করা পর্যন্ত কন্টেইনারটি পুরনো কন্টেন্টই দেখতে থাকে। ফাইলটি ঘনঘন এডিট করা হলে প্যারেন্ট ডিরেক্টরিটি মাউন্ট করুন।

পারফরম্যান্স: যেখানে পার্থক্যটি বাস্তব

Linux সার্ভারে উভয় ধরনের ভলিউমই একই কার্নেল পাথ ব্যবহার করে, তাই থ্রুপুটের পার্থক্য এতই সামান্য যে এই ভিত্তিতে আপনার কোনোটিকে বেছে নেওয়ার প্রয়োজন নেই। ডিফল্ট local ড্রাইভার ব্যবহার করা নেমড ভলিউমগুলো Docker-এর বাকি অংশের মতো একই ফাইলসিস্টেমে, /var/lib/docker/volumes/-এর অধীনে থাকে এবং বাইন্ড মাউন্ট আপনি যেখানে নির্দেশ করেছেন সেখানেই থাকে।

macOS এবং Windows-এর জন্য Docker Desktop-এ এই পার্থক্যটি স্পষ্ট হয়ে ওঠে, যেখানে কন্টেইনারগুলো একটি ভার্চুয়াল মেশিনের ভেতরে চলে। সেখানে একটি বাইন্ড মাউন্ট হোস্ট ফাইলসিস্টেম থেকে ফাইল শেয়ারিং লেয়ারের মাধ্যমে ভার্চুয়াল মেশিনে প্রবেশ করে। ফলে অনেক ছোট ছোট ফাইল অপারেশন রয়েছে এমন ওয়ার্কলোড, যেমন Node.js ডিপেন্ডেন্সি ট্রি বা PHP ফ্রেমওয়ার্ক ক্যাশ, উল্লেখযোগ্যভাবে ধীর হয়ে যায়। নেমড ভলিউমগুলো ভার্চুয়াল মেশিনের ভেতরেই থাকে এবং এই বাড়তি খরচ বহন করতে হয় না। এই কারণেই অনেক ডেভেলপমেন্ট compose ফাইলে সোর্স ডিরেক্টরিকে বাইন্ড-মাউন্ট করা হয় কিন্তু node_modules-এর ওপর একটি নেমড ভলিউম ঘোষণা করা হয়।

অন্য যে বাস্তব পার্থক্যটি রয়েছে তা হলো বাইটগুলো কোথায় জমা হয়। /mnt/backup-এ একটি বাইন্ড মাউন্ট ডেটাকে সেই ডিস্কে রাখে। একটি নেমড ভলিউম এমন যেকোনো ফাইলসিস্টেমে জমা হয় যেখানে /var/lib/docker থাকে, যা একটি VPS-এর ক্ষেত্রে সাধারণত রুট ডিস্ক হয়। একটি নেমড ভলিউমের ভেতরে ডেটাবেস বড় হতে থাকলে তা আপনার সিস্টেম লগ যে ডিস্কে আছে সেটিকেই পূর্ণ করে ফেলে। কোনো ইনসিডেন্ট ঘটার আগেই এটি পরীক্ষা করুন:

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

docker system df -v প্রতিটি ভলিউমকে তার আকারসহ তালিকাভুক্ত করে এবং যেগুলোকে আর কোনো কন্টেইনার রেফার করছে না সেগুলোকে চিহ্নিত করে।

নেমড ভলিউম পরিদর্শন করা

একটি নেমড ভলিউম কোনো ব্ল্যাক বক্স নয়। এটি কোথায় আছে তা জানতে Docker-কে জিজ্ঞাসা করুন:

docker volume inspect myapp_pgdata

Mountpoint ফিল্ডটি একটি প্রকৃত হোস্ট পাথ প্রদান করে, যা সাধারণত /var/lib/docker/volumes/myapp_pgdata/_data হয়। আপনি sudo ls ব্যবহার করে এটি পড়তে পারেন এবং দ্রুত যাচাইয়ের জন্য এটি কার্যকর। এটিকে ফাইল সম্পাদনা করার জায়গা হিসেবে ব্যবহার করবেন না। root হিসেবে সেখানে কিছু লিখলে উপরে বর্ণিত মালিকানা সংক্রান্ত সমস্যার পুনরাবৃত্তি ঘটে এবং এই পাথটি local ড্রাইভারের একটি অভ্যন্তরীণ বিষয়, যা অন্যান্য ভলিউম ড্রাইভারের ক্ষেত্রে প্রযোজ্য নয়।

ভলিউমের ভেতরে দেখার নিরাপদ উপায় হলো একটি অস্থায়ী কন্টেইনার ব্যবহার করা যা ভলিউমটিকে মাউন্ট করে:

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

এটি যেকোনো ড্রাইভারের ক্ষেত্রেই কাজ করে, প্রকৃত কন্টেইনার যে পারমিশনগুলো দেখে এটিও তা-ই দেখে এবং --rm ব্যবহারের কারণে এটি কোনো অবশিষ্ট ফাইল রেখে যায় না।

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

একটি bind mount হলো একটি সাধারণ ডিরেক্টরি, তাই যেকোনো ফাইল-লেভেল ব্যাকআপ টুল এটি সহজেই হ্যান্ডেল করতে পারে। হোস্ট পাথের দিকে ব্যাকআপ নির্দেশ করুন এবং আপনার কাজ শেষ। একটি named volume-এর ক্ষেত্রে একটি অতিরিক্ত ধাপ প্রয়োজন, কারণ টুলটিকে এর ভেতরে প্রবেশ করতে হয়। একই স্বল্পস্থায়ী কন্টেইনারে ভলিউম এবং একটি হোস্ট ডিরেক্টরি মাউন্ট করুন, তারপর একটি আর্কাইভ তৈরি করুন:

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

এটি উল্টে দিয়ে একটি নতুন ভলিউমে রিস্টোর করুন:

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 কন্টেইনারের ভেতরে root হিসেবে চলার সময় সংখ্যাসূচক মালিকানা (numeric ownership) বজায় রাখে, যা রিস্টোর করা ভলিউমটিকে অ্যাপ্লিকেশনের ব্যবহারের উপযোগী রাখে।

উভয় ধরনের ক্ষেত্রেই একটি সতর্কতা প্রযোজ্য। ডাটাবেস চলার সময় এর ফাইলগুলো কপি করলে আপনি একটি পরিবর্তনশীল অবস্থার আর্কাইভ পাবেন, যা রিস্টোর করার সময় ডাটাবেসটিকে ক্ষতিগ্রস্ত (corrupt) করতে পারে। প্রথমে সার্ভিসটি বন্ধ করুন, অথবা docker compose exec -T db pg_dump -U postgres appdb > appdb.sql-এর মতো ডাটাবেসের নিজস্ব টুলের মাধ্যমে ডাম্প করুন। এটি একটি সাধারণ ফাইল তৈরি করবে যা আপনি আপনার compose ফাইলগুলোর পাশাপাশি একটি সাধারণ এনক্রিপ্ট করা restic ব্যাকআপ রুটিন-এ অন্তর্ভুক্ত করতে পারবেন।

একটি bind mount-কে named volume-এ স্থানান্তর করা

এই স্থানান্তরটি একটি কপি প্রক্রিয়া, রিনেম নয়, এবং এটি সম্পন্ন করতে প্রায় এক মিনিট সময় লাগে।

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 মালিকানা, মোড এবং টাইমস্ট্যাম্প বজায় রাখে, তাই যে কন্টেইনার ব্যবহারকারী পুরনো ডিরেক্টরিটি পড়তে পারতেন, তিনি নতুন ভলিউমটিও পড়তে পারবেন। এরপর সার্ভিসটিকে pgdata:/var/lib/postgresql/data ব্যবহার করার জন্য পরিবর্তন করুন, টপ-লেভেল volumes: ব্লকে pgdata যোগ করুন, docker compose up -d রান করুন এবং পুরনো ডিরেক্টরিটি মুছে ফেলার আগে অ্যাপ্লিকেশনের লগগুলো পরীক্ষা করুন। বিপরীত প্রক্রিয়ার ক্ষেত্রে একই কমান্ড ব্যবহার করুন, তবে /from এবং /to এর স্থান অদলবদল করে নিন।

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

পরিষেবা অনুযায়ী নির্বাচন

ফাইলটি কে তৈরি করছে তা যাচাই করুন। যে কনফিগারেশন আপনি টেক্সট এডিটরে পরিবর্তন করেন এবং git-এ কমিট করেন, তা একটি bind mount-এ রাখা উচিত। এটি :ro-এ মাউন্ট করুন, কারণ আপনি চান ফাইলটি দৃশ্যমান থাকুক এবং এর ভার্সন কন্ট্রোল থাকুক। অ্যাপ্লিকেশনের স্টেট, যা আপনি কখনোই নিজে থেকে খোলেন না, তা একটি named volume-এ রাখা উচিত। কারণ Docker সঠিকভাবে পারমিশন সেট করে এবং ডেটা কোনো নির্দিষ্ট host path-এর ওপর নির্ভর করে না।

মিশ্র ক্ষেত্রের উদাহরণ হলো মিডিয়া। একটি ফটো লাইব্রেরি অ্যাপ্লিকেশন দ্বারা তৈরি হয়, কিন্তু আপনি নিজেও তা পরিচালনা করেন। এটি প্রায়শই এত বড় হয় যে এর জন্য আলাদা ডিস্কের প্রয়োজন হয়। সেটিকে ওই ডিস্কের একটি পাথে bind mount করুন এবং একবার সুপরিকল্পিতভাবে এর মালিকানা (ownership) নির্ধারণ করুন। বেশিরভাগ সেলফ-হোস্টেড স্ট্যাক এই পদ্ধতি অনুসরণ করে: ডেটাবেস এবং ক্যাশের জন্য named volumes, এবং কনফিগারেশন ও গুরুত্বপূর্ণ বড় ডিরেক্টরির জন্য bind mounts।

FAQ

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

একটি bind mount হোস্টের একটি পাথকে কন্টেইনারের ভেতরে ম্যাপ করে, ফলে উভয় প্রান্তেই একই ডিরেক্টরি দেখা যায় এবং আপনি সাধারণ টুল ব্যবহার করে তা এডিট করতে পারেন। একটি named volume হলো এমন স্টোরেজ যা Docker তৈরি ও পরিচালনা করে, যাকে নাম দিয়ে চিহ্নিত করা হয় এবং টপ-লেভেল volumes: ব্লকে ঘোষণা করা হয়। এদের মধ্যে ব্যবহারিক পার্থক্য হলো মালিকানা: আপনার রক্ষণাবেক্ষণ করা কনফিগারেশনের জন্য bind mount এবং অ্যাপ্লিকেশনের রক্ষণাবেক্ষণ করা ডেটার জন্য named volume ব্যবহার করুন।

bind mount-এ "permission denied" ত্রুটি কেন আসে, কিন্তু named volume-এ আসে না?

একটি খালি named volume ইমেজ থেকে তৈরি হয়, তাই এটি ইমেজে সেট করা মালিকানা গ্রহণ করে এবং কন্টেইনার ব্যবহারকারী এতে লিখতে পারেন। একটি bind mount হোস্টের ডিরেক্টরিকে হুবহু প্রদর্শন করে এবং যদি Docker-কে সেই ডিরেক্টরি তৈরি করতে হয়, তবে সেটির মালিকানা root-এর অধীনে থাকে। কন্টেইনার যে সংখ্যাসূচক আইডি ব্যবহার করে তা দেখতে docker compose exec <service> id চালান, তারপর হোস্ট ডিরেক্টরিতে sudo chown -R <uid>:<gid> করুন, অথবা সার্ভিসে user: "1000:1000" সেট করুন।

Docker ডিস্কে named volume কোথায় সংরক্ষণ করে?

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

আমি কীভাবে একটি named volume-এর ব্যাকআপ নেব?

একটি স্বল্পস্থায়ী কন্টেইনার চালান যেখানে ভলিউম এবং একটি হোস্ট ডিরেক্টরি উভয়ই মাউন্ট করা থাকবে, তারপর docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data . ব্যবহার করে একটি থেকে অন্যটিতে আর্কাইভ করুন। ডেটাবেসের ক্ষেত্রে, সরাসরি ফাইল কপি না করে ডেটাবেসের নিজস্ব টুল দিয়ে ডাম্প নিন। কারণ রাইট অপারেশন চলাকালীন ফাইল কপি করলে তা রিস্টোর করার সময় ডেটা করাপ্ট হয়ে যেতে পারে।

docker compose down কি আমার ভলিউমগুলো মুছে ফেলে?

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