SSD Nodes Learn
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-07-25

VPS-এ Docker Compose সেটআপ করুন Ubuntu 24.04-এ

Ubuntu 24.04-এ Docker Engine ও Compose v2 ইনস্টল করুন, একটি আসল two-service compose.yml লিখুন। ufw পোর্ট প্রকাশনা ফাঁক এবং ভলিউম ব্যাকআপ সম্পর্কে বিস্তারিত জানুন।

আপনি যা তৈরি করছেন

Docker Compose হলো এই সাইটের প্রায় সবকিছুরই ভিত্তি। Nextcloud, Vaultwarden, n8n, Immich, Rocket.Chat — এই প্রতিটি গাইডই শুরু হয় "এই compose ফাইলটি লিখুন" বলে, আর এই পেজটি ব্যাখ্যা করে সেই ফাইলের প্রকৃত অর্থ কী। আপনি Ubuntu 24.04-এ Docker-এর নিজস্ব apt রিপোজিটরি থেকে Docker Engine এবং Compose v2 প্লাগইন ইনস্টল করবেন, তারপর একটি আসল two-service স্ট্যাক চালু করবেন — Miniflux, একটি ছোট RSS রিডার, এবং PostgreSQL — কারণ এই জুটিটি বড় অ্যাপগুলোর প্রতিটি প্যাটার্ন ব্যবহার করে: pinned ইমেজ, healthcheck সহ একটি ডেটাবেস, একটি named ভলিউম, .env ফাইলে সিক্রেট, এবং শুধু localhost-এ প্রকাশিত একটি পোর্ট।

ইনস্টলে পাঁচ মিনিট লাগে। এই গাইডের বাকি অংশ সেসব বিষয় নিয়ে আলোচনা করে যা পরে সমস্যা তৈরি করে: docker গ্রুপ যা আসলে অন্য নামে root, প্রকাশিত পোর্টগুলো আপনার ufw নিয়ম সরাসরি এড়িয়ে যায়, এবং docker compose down-এর সেই একটি ফ্ল্যাগ যা কোনো নিশ্চিতকরণ ছাড়াই আপনার ডেটাবেস মুছে দেয়।

পূর্বশর্ত: একটি নতুন Ubuntu 24.04 KVM VPS, sudo সহ একজন ব্যবহারকারী, এবং এক গিগাবাইট বা তার বেশি RAM। পুরোনো Docker ইনস্টল থাকলেও সমস্যা নেই — প্রথম বিভাগে কী সরাতে হবে তা আলোচনা করা হয়েছে।

Docker-এর রিপোজিটরি থেকে ইনস্টল করুন, Ubuntu-এর নিজস্ব রিপোজিটরি থেকে নয়

প্রথম কমান্ডের আগে দুটি ভুল পথ এড়িয়ে যেতে হবে। Ubuntu-এর নিজস্ব docker.io প্যাকেজটি কাজ করে, কিন্তু Docker-এর রিলিজের তুলনায় পিছিয়ে থাকে এবং এতে প্লাগইন লেআউটের অভাব থাকে, যা অন্যান্য সবকিছু ধরে নিয়ে কাজ করে। আর স্ট্যান্ডঅ্যালোন docker-compose বাইনারিটি — যেটিতে হাইফেন আছে — সেটি হলো Compose v1: এটি Python-ভিত্তিক, 2023 সাল থেকে এর জীবনচক্র শেষ, এবং পুরোনো টিউটোরিয়ালগুলো ব্যর্থ হওয়ার কারণ এটিই। বর্তমানে Compose হলো docker compose, যেখানে স্পেস ব্যবহার করা হয়; এটি একটি CLI প্লাগইন, যা ইঞ্জিনের মতো একই রিপোজিটরি থেকে ইনস্টল করা হয়।

এর মধ্যে কোনোটি যদি সার্ভারে আগে থেকেই থাকে, তবে প্রথমে সেগুলো মুছে ফেলুন — প্লাগইনের Ubuntu-এর নিজস্ব প্যাকেজ docker-compose-v2 সহ — যাতে সবকিছু একটি রিপোজিটরি থেকে আসে:

sudo apt remove -y docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc

নতুন VPS-এ Package 'docker.io' is not installed, so not removed হলো স্বাভাবিক আউটপুট। এরপর Docker-এর রিপোজিটরি যোগ করে ইনস্টল করুন:

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

তিনটি স্তরই যাচাই করুন:

docker --version
docker compose version
sudo docker run --rm hello-world

প্রথম দুটি ভার্সন স্ট্রিং প্রিন্ট করে — Docker Compose version v2.x.x নিশ্চিত করে যে আপনার কাছে প্লাগইন আছে, বাতিল v1 বাইনারি নয়। hello-world রানটি Hello from Docker! দিয়ে শেষ হওয়া উচিত। প্যাকেজটি বুটের সময় সার্ভিসটি চালু করে; systemctl is-enabled docker প্রিন্ট করে enabled

docker গ্রুপই হলো root — চোখ খুলে সিদ্ধান্ত নিন

এই মুহূর্তে প্রতিটি docker কমান্ডের জন্য sudo প্রয়োজন, কারণ /var/run/docker.sock-এ অবস্থিত ডেমনের সকেটটি root এবং docker গ্রুপের মালিকানাধীন। গ্রুপের সদস্যপদ না থাকলে আপনি সবচেয়ে বেশি গুগলে খোঁজা Docker ত্রুটিটি পাবেন:

permission denied while trying to connect to the Docker daemon socket at
unix:///var/run/docker.sock

প্রমিত সমাধান:

sudo usermod -aG docker $USER

গ্রুপের সদস্যপদ লগইনের সময় কার্যকর হয়, তাই আপনার বর্তমান শেলে ত্রুটিটি থেকে যাবে। এই সেশনের জন্য newgrp docker চালান, অথবা লগআউট করে আবার লগইন করুন; এরপর id আপনার গ্রুপের তালিকায় docker দেখাবে।

এখন সরাসরি সত্যটি: docker গ্রুপের সদস্যপদ মানেই হোস্টে root। "root-সদৃশ" নয়, "উন্নত অধিকার" নয় — root। ওই গ্রুপের যে কেউ docker run --rm -it -v /:/host alpine chroot /host চালাতে পারে এবং সম্পূর্ণ ফাইলসিস্টেমের মালিক হতে পারে, কোনো পাসওয়ার্ড ছাড়াই। এই গ্রুপটি সুবিধার জন্য রয়েছে, সীমাবদ্ধ করার জন্য নয়।

Docker-এর rootless মোডই হলো আসল বিকল্প — ডেমন নিজে আপনার অবিশেষাধিকারপ্রাপ্ত ব্যবহারকারী হিসেবে চলে। এর একটি মূল্য আছে: 1024-এর নিচের পোর্টগুলির জন্য অতিরিক্ত সেটআপ প্রয়োজন, নেটওয়ার্কিং একটি ইউজারস্পেস শিমের মাধ্যমে চলে যার পরিমাপযোগ্য ওভারহেড রয়েছে, এবং কিছু ইমেজ আসল root ছাড়া সঠিকভাবে কাজ করে না। একটি একক-অ্যাডমিন VPS-এ, যেখানে একমাত্র লগইন ব্যবহারকারীর কাছে ইতিমধ্যে sudo রয়েছে, এই গ্রুপটি বাস্তবে কিছুই পরিবর্তন করে না, এবং এখানকার প্রতিটি গাইড এটিকেই ধরে নেয় — কেবল এটিকে sudo-এর চেয়ে কম কিছু ভেবে কাউকে দেবেন না।

একটি compose ফাইলের গঠন

প্রতিটি stack-কে তার নিজস্ব ডিরেক্টরি দিন — ডিরেক্টরির নামটিই প্রজেক্টের নাম হয়, যা container, network এবং volume-এর নামের উপসর্গ হিসেবে ব্যবহৃত হয়:

sudo mkdir -p /opt/miniflux && sudo chown $USER /opt/miniflux && cd /opt/miniflux

compose.yml তৈরি করুন (এটি বর্তমান নাম; docker-compose.yml এখনও কাজ করে)। পুরোনো version: কী-টি এড়িয়ে যান — এটি অপ্রচলিত এবং Compose এটি দেখলে সতর্ক করে।

services:
  miniflux:
    image: miniflux/miniflux:2.2.9
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      - DATABASE_URL=postgres://miniflux:${POSTGRES_PASSWORD}@db/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin
      - ADMIN_PASSWORD=${ADMIN_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=miniflux
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "miniflux", "-d", "miniflux"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  db-data:

উপরের প্রতিটি লাইন একেকটি সিদ্ধান্ত। সেগুলো একটি করে বিবেচনা করুন।

ইমেজ ভার্সন পিন করুন — :latest এবং একটি pull মানে হলো অনিয়ন্ত্রিত আপগ্রেড

postgres:16-alpine ব্যবহার করুন, postgres:latest নয়। একটি ট্যাগ স্থির নয়: আপনি প্রতিবার pull করার সময় :latest আবার মেইনটেইনারের সবচেয়ে সাম্প্রতিক পাবলিশ করা ভার্সনে রূপান্তরিত হয়। এর সাথে সেই রুটিন আপগ্রেড অভ্যাসটি যোগ করুন যা আপনি এখন শিখতে চলেছেন — docker compose pull && docker compose up -d — এবং :latest মানে হলো আপস্ট্রিম যখনই মেজর ভার্সন রিলিজ করবে, সেটি তখনই আপনার সিস্টেমে চলে আসবে, আপনার ইচ্ছায় নয়। PostgreSQL-এর ক্ষেত্রে এটি কোনো কাল্পনিক ধারণা নয়: 16 থেকে 17-এ একটি অপ্রত্যাশিত লাফ কন্টেইনারটিকে একটি অসঙ্গত ডেটা ডিরেক্টরিতে crash-loop করায়, কারণ Postgres-এর মেজর আপগ্রেডের জন্য dump এবং restore প্রয়োজন, শুধু পুনরায় চালু করা নয়।

অন্তত মেজর ভার্সন পিন করুন (postgres:16-alpine 16.x প্যাচ রিলিজগুলো অনুসরণ করে), এবং অ্যাপ্লিকেশনগুলোকে miniflux/miniflux:2.2.9-এর মতো একটি নির্দিষ্ট রিলিজে পিন করুন — প্রজেক্টের রিলিজ পেজ দেখুন এবং ফাইলটি লেখার সময় বর্তমান যা আছে তা ব্যবহার করুন। এরপর একটি আপগ্রেড হয়ে ওঠে আপনার ইচ্ছাকৃত এক লাইনের পরিবর্তন, যা git diff-এ দৃশ্যমান।

127.0.0.1-এ পাবলিশ করুন, কারণ Docker ufw-কে এড়িয়ে যায়

"127.0.0.1:8080:8080" — হোস্ট অ্যাড্রেস, হোস্ট পোর্ট, কন্টেইনার পোর্ট। বেশিরভাগ টিউটোরিয়ালে "8080:8080" লেখা থাকে, যা 0.0.0.0:8080:8080-এর সংক্ষিপ্ত রূপ: প্রতিটি ইন্টারফেসে শোনা, যার মধ্যে পাবলিক ইন্টারফেসও রয়েছে।

এখানেই হলো ফাঁদ, এবং এটি প্রায় সবাইকে একবার কামড়ায়। Docker একটি পোর্ট পাবলিশ করার সময় একটি DNAT রুল লেখে, যা ফিল্টারিং-এর আগে প্যাকেটের গন্তব্যকে কন্টেইনারের অভ্যন্তরীণ IP-এ পুনরায় লেখে, তাই প্যাকেটটি FORWARD পথ ধরে এবং INPUT-কে কখনও স্পর্শ করে না, যেখানে আপনার ufw রুলগুলো থাকে। sudo ufw deny 8080 সফল হওয়ার খবর দেয়, ufw status পোর্টটিকে ব্লক করা দেখায়, এবং সার্ভিসটি এখনও পুরো ইন্টারনেটের কাছে সাড়া দেয়। আপনার ফায়ারওয়াল নষ্ট হয়নি; এটি ডিজাইন অনুযায়ী বাইপাস করা হচ্ছে। কেন Docker ufw বাইপাস করে, এবং কন্টেইনার ট্রাফিক সত্যিকার অর্থে কীভাবে ফিল্টার করবেন প্রক্রিয়াটি এবং যেসব পোর্ট পাবলিক রাখতে হবে সেগুলোর জন্য DOCKER-USER সমাধান বিস্তারিতভাবে আলোচনা করে।

পুরো সমস্যাটিকে মুছে দেওয়ার অভ্যাস: প্রকাশিত পোর্টগুলোকে 127.0.0.1-এ বাইন্ড করুন, যদি না এটি না করার জন্য আপনার কাছে নির্দিষ্ট কোনো কারণ থাকে, এবং এমন কিছুর জন্য যা বাইরের দুনিয়ার মুখোমুখি হওয়া উচিত সেটির সামনে একটি রিভার্স প্রক্সি রাখুন। এটিই ঠিক সেই জিনিস যা Traefik রিভার্স প্রক্সি গাইড এই পেজের পরবর্তী ধাপ হিসেবে তৈরি করে — একটি মাত্র কন্টেইনার যা পোর্ট 80 এবং 443 নিয়ন্ত্রণ করে এবং hostname অনুযায়ী TLS সহ বাকি সবকিছুতে রাউট করে। (পুরোনো একটি Traefik v2 সেটআপ থেকে আসছেন? Traefik v2 থেকে v3 মাইগ্রেশন গাইড নাম পরিবর্তন এবং রুল পরিবর্তনগুলো কভার করে।)

stack শুরু করার পর বাইন্ড যাচাই করুন: sudo ss -tlnp | grep 8080-এ 127.0.0.1:8080 দেখানো উচিত, 0.0.0.0:8080 বা *:8080 নয়।

নেমড ভলিউম বনাম বাইন্ড মাউন্ট

db-data:/var/lib/postgresql/data একটি নেমড ভলিউম: Docker /var/lib/docker/volumes/-এর অধীনে একটি ডিরেক্টরি তৈরি ও পরিচালনা করে এবং সেটি কন্টেইনারে মাউন্ট করে। বিকল্প হলো একটি বাইন্ড মাউন্ট, ./data:/var/lib/postgresql/data, যা হোস্টে আপনার বেছে নেওয়া একটি পথকে ম্যাপ করে।

ব্যবহারিকভাবে যে বিভাজনটি কাজ করে: শুধুমাত্র কন্টেইনার যে ডেটা ব্যবহার করে সেটির জন্য নেমড ভলিউম — বিশেষ করে ডেটাবেস, কারণ Docker ভলিউমটিকে ইমেজের প্রত্যাশিত মালিকানা দিয়ে শুরু করে এবং ফাইল পারমিশনগুলো ঠিকঠাকভাবে কাজ করে। হোস্ট থেকে আপনি যে ফাইলগুলো ব্যবহার করেন সেগুলোর জন্য বাইন্ড মাউন্ট — কনফিগ ফাইল যা আপনি একটি টেক্সট এডিটর দিয়ে সম্পাদনা করেন, একটি মিডিয়া লাইব্রেরি যা আপনি rsync করেন, এমন কিছু যার পথ আপনি স্পষ্ট রাখতে চান। বাইন্ড মাউন্টের সাধারণ ব্যর্থতা হলো মালিকানা: কন্টেইনারটি UID 999 হিসেবে চলে, আপনার হোস্ট ডিরেক্টরির মালিকানা UID 1000-এর, এবং অ্যাপটি শুরুতেই তার লগে permission denied দেখিয়ে বন্ধ হয়ে যায়। নেমড ভলিউম সেই ধরনের বাগ বেশিরভাগই মুছে দেয়, এর খরচ হলো ডেটা Docker-পরিচালিত একটি পথে থাকে — যা নিচে আলোচনা করা হয়েছে।

environment এবং .env — git থেকে সিক্রেট দূরে রাখুন

${POSTGRES_PASSWORD} আপনার শেল থেকে পড়া হয় না; Compose এটি compose.yml-এর পাশে অবস্থিত .env নামের একটি ফাইল থেকে ইন্টারপোলেট করে। এটি তৈরি করুন:

cat > .env <<'EOF'
POSTGRES_PASSWORD=change-me-to-something-long
ADMIN_PASSWORD=change-me-too
EOF
chmod 600 .env
echo ".env" >> .gitignore

openssl rand -hex 24 দিয়ে আসল মান তৈরি করুন। ইচ্ছাকৃতভাবেই hex, base64 নয়: এই পাসওয়ার্ডটি DATABASE_URL কানেকশন স্ট্রিং-এর ভেতরে থাকে, এবং base64 যে /, +, এবং = ক্যারেক্টার তৈরি করে তা URL পার্সিং নষ্ট করে — এমন একটি ব্যর্থতা যা একটি সিনট্যাক্স এরর হিসেবে নয়, বরং একটি অথেনটিকেশন এরর হিসেবে দেখা দেয়, এবং এর জন্য একটি সন্ধ্যা নষ্ট হয়। .gitignore লাইনটি প্রথম কমিটের আগে যোগ করুন: compose ফাইলটি পাবলিশ এবং ভার্সন করা নিরাপদ, কিন্তু .env ফাইলটি কখনও নয়, এবং যে সিক্রেট git হিস্ট্রি স্পর্শ করেছে সেটি আপনাকে রোটেট করতে হবে। আপনি যদি একটি ভেরিয়েবল বাদ দিয়ে stack শুরু করেন, Compose জোরে সতর্ক করে এবং একটি খালি স্ট্রিং দিয়ে চলতে থাকে — যার অর্থ একটি Postgres পাসওয়ার্ডের জন্য হলো একটি ভাঙা ডিপ্লয়মেন্ট:

WARN[0000] The "POSTGRES_PASSWORD" variable is not set. Defaulting to a blank string.

docker compose config সম্পূর্ণ ইন্টারপোলেটেড ফাইলটি প্রিন্ট করে — কন্টেইনারগুলো আসলে কী গ্রহণ করবে তা যাচাই করার সবচেয়ে দ্রুত উপায়; মনে রাখবেন এর আউটপুটে আপনার সিক্রেটগুলো অন্তর্ভুক্ত থাকে।

depends_on কিছুর জন্য অপেক্ষা করে না — যদি না আপনি একটি healthcheck যোগ করেন

একটি খালি depends_on: [db] শুধুমাত্র শুরুর ক্রম নিয়ন্ত্রণ করে: Compose প্রথমে Postgres চালু করে এবং কিছুক্ষণ পরে অ্যাপটি, অথচ তখনও কানেকশন গ্রহণ করতে Postgres-এর কয়েক সেকেন্ড দূরে। অ্যাপটি ডেটাবেসে পৌঁছানোর চেষ্টা করে, ব্যর্থ হয়, এবং এটি কতটা ভালোভাবে লেখা হয়েছে তার ওপর নির্ভর করে ক্র্যাশ করে বা পুনরায় চেষ্টা করে।

নির্ভরযোগ্য ভার্সনটি হলো উপরের ফাইলটি যা ব্যবহার করে: db সার্ভিসটি একটি healthcheck নির্ধারণ করে (Postgres ঠিক এই কাজের জন্যই pg_isready দেয়), এবং অ্যাপটি depends_on-এর সাথে condition: service_healthy ঘোষণা করে। Compose ডেটাবেস শুরু করে, প্রতি 10 সেকেন্ডে চেকটি পোল করে, এবং চেকটি সফল হলেই কেবল Miniflux শুরু করে। যদি ডেটাবেস কখনও সুস্থ না হয় — ভুল পাসওয়ার্ড, নষ্ট ভলিউম — অ্যাপটি কখনও শুরু হয় না এবং Compose আপনাকে জানায় কোন ডিপেন্ডেন্সি ব্যর্থ হয়েছে:

dependency failed to start: container miniflux-db-1 is unhealthy

সেই বার্তাটি আপনাকে docker compose logs db-এর দিকে নির্দেশ করে, যেখানে আসল এররটি থাকে।

restart: unless-stopped

উভয় সার্ভিসে restart: unless-stopped মানে হলো কন্টেইনারগুলো একটি ক্র্যাশের পরে এবং একটি VPS রিবুটের পরে ফিরে আসে, কিন্তু আপনি ইচ্ছাকৃতভাবে docker compose stop চালালে সেগুলো বন্ধ থাকে। বিকল্প always ম্যানুয়াল স্টপের পরেও কন্টেইনারগুলোকে পুনরুজ্জীবিত করে — যা কদাচিৎই আপনার উদ্দেশ্য ছিল। একটি রিস্টার্ট পলিসি ছাড়া, ভোর 4টায় একটি কার্নেল-আপডেট রিবুট চুপচাপ আপনার সার্ভিসগুলোকে বন্ধ করে দেয় যতক্ষণ না আপনি খেয়াল করেন।

দৈনন্দিন ক্রিয়া

দিনের সব কাজ মোট পাঁচটি কমান্ডে হয়, যেগুলো প্রজেক্ট ডিরেক্টরি থেকে চালাতে হয়।

docker compose up -d        # create and start; idempotent, recreates only what changed
docker compose ps           # status, ports, and health of this project's containers
docker compose logs -f miniflux   # follow one service's logs; --tail 100 for recent history
docker compose pull && docker compose up -d   # upgrade to the pinned tags
docker compose down         # stop and remove containers and the network

up -d বারবার চালানো নিরাপদ — এটি ফাইলের সাথে বাস্তব অবস্থা তুলনা করে এবং শুধুমাত্র সেইসব সার্ভিস ছোঁয় যাদের কনফিগ বা ইমেজ বদলেছে। আপগ্রেড জোড়া আপনার পিন করা ট্যাগগুলো এখন যে পয়েন্টে আছে সেটাই আনে: postgres:16-alpine-এর অধীনে প্যাচ রিলিজ, আর সুনির্দিষ্ট পিনের ক্ষেত্রে আপনি না এডিট করা পর্যন্ত কিছুই আসে না — এটাই এর উদ্দেশ্য। আপগ্রেডের পর পুরোনো ইমেজ জমতে থাকে; docker image prune -f দিয়ে ডিস্ক পুনরুদ্ধার করুন।

এবার ধ্বংসাত্মকটি, স্পষ্টভাবে বলা হলো: docker compose down নিরাপদ — কন্টেইনার এবং নেটওয়ার্ক ব্যবহারের পরে ফেলে দেওয়া যায়, আর আপনার ডেটা ভলিউমে আছে। docker compose down -v নাম দেওয়া ভলিউমগুলোও মুছে দেয়। সেটাই আপনার ডেটাবেস, মুহূর্তে গায়েব, কোনো নিশ্চিতকরণ প্রম্পট ছাড়া, কোনো পূর্বাবস্থায় ফেরার উপায় ছাড়া। -v ফ্ল্যাগ পরীক্ষামূলক কাজ ভেঙে ফেলার জন্য রয়েছে; আসল ডেটা ধরে রাখা স্ট্যাকে এটিকে আপনি rm -rf যেভাবে দেখেন সেভাবেই দেখুন। /var/lib/docker/volumes/-এর নিচে কোনো ডাস্টবিন নেই।

চলমান কন্টেইনারের ভেতরে একবারের জন্য শেল চাইলে: docker compose exec db psql -U miniflux আপনাকে ডেটাবেসে নামিয়ে দেয়, আর docker compose exec miniflux sh আপনাকে অ্যাপে একটি শেল দেয়।

আপনার ডেটা আসলে কোথায় থাকে

Named volume-গুলো প্রজেক্ট প্রিফিক্স পায়। তাই db-data নামের একটি ডিরেক্টরিতে miniflux হয়ে যায় miniflux_db-data:

docker volume ls
docker volume inspect miniflux_db-data

inspect আউটপুটে গুরুত্বপূর্ণ লাইনটি থাকে:

"Mountpoint": "/var/lib/docker/volumes/miniflux_db-data/_data"

সেই ডিরেক্টরিটিই হলো ডেটাবেস। এটি host ফাইলসিস্টেমে root-এর মালিকানাধীন। এটি down, আপগ্রেড এবং কন্টেইনার পুনর্নির্মাণের পরেও টিকে থাকে। আপনার ব্যাকআপগুলোরও ঠিক এটিকেই ধরে রাখা উচিত।

একটি নামকৃত ভলিউম ব্যাকআপ করুন

প্রচলিত পদ্ধতিটি হলো একটি বর্জনযোগ্য কন্টেইনার ব্যবহার করা, যা ভলিউমটিকে রিড-ওনলি হিসেবে একটি হোস্ট ডিরেক্টরির পাশে মাউন্ট করে এবং টার করে বের করে:

docker run --rm \
  -v miniflux_db-data:/data:ro \
  -v "$PWD":/backup \
  alpine:3.22 tar czf /backup/miniflux-db-$(date +%F).tar.gz -C /data .

এতে কোনো ইনস্টলেশনের প্রয়োজন নেই, কিছুই চলমান অবস্থায় থাকে না, এবং পুনরুদ্ধার প্রক্রিয়াটি এর ঠিক উল্টো — tar xzf একটি নতুন খালি ভলিউমে মাউন্টগুলো উল্টোভাবে প্রয়োগ করে।

ডেটাবেসের ক্ষেত্রে একটি সতর্কতা: চলমান অবস্থায় থাকা একটি Postgres ডেটা ডিরেক্টরিকে টার করলে মাঝপথে লেখার এমন একটি অবস্থা সংরক্ষিত হতে পারে যা পরিষ্কারভাবে শুরু হবে না। টার হওয়ার কয়েক সেকেন্ডের জন্য হয় docker compose stop করুন, অথবা — আরও ভালো উপায় — একটি লজিক্যাল ডাম্প নিন, যা স্বভাবতই সামঞ্জস্যপূর্ণ:

docker compose exec -T db pg_dump -U miniflux miniflux | gzip > miniflux-$(date +%F).sql.gz

-T টি Compose-এর ডিফল্টভাবে তৈরি করা সিউডো-টার্মিনালটিকে নিষ্ক্রিয় করে — একটি TTY-এর মাধ্যমে ডাম্প আউটপুট পাইপ করলে তা নষ্ট হতে পারে। এগুলোর মধ্যে যেকোনো একটি cron-এ যুক্ত করুন এবং ফলাফলটি VPS থেকে অন্যত্র কপি করে রাখুন; যে ডেটা সুরক্ষিত করছেন তার একই ডিস্কে রাখা ব্যাকআপটি হলো একটি অনুলিপি, ব্যাকআপ নয়। Nextcloud গাইড ঠিক এই দুটি পদ্ধতির ওপর ভিত্তি করে একটি সম্পূর্ণ নির্ধারিত রুটিন তৈরি করেছে।

ব্যর্থতার ধরন, আপনি যে স্ট্রিংগুলি দেখবেন

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock — আপনি এখনও docker গ্রুপে নেই, অথবা আছেন কিন্তু সেশনটি তার আগের। id আপনার কার্যকর গ্রুপগুলি দেখায়; newgrp docker বর্তমান শেল ঠিক করে, লগ আউট করে আবার লগ ইন করলে সব ঠিক হয়।

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? — ভিন্ন সমস্যা: ডেমনটি নিজেই বন্ধ আছে। sudo systemctl status docker এবং sudo journalctl -u docker -n 50 কারণ বলে। একটি VPS-এ প্রচলিত কারণ হলো ডিস্ক পূর্ণ — প্রথমে df -h /var/lib/docker দেখুন।

Bind for 127.0.0.1:8080 failed: port is already allocated — অন্য একটি কন্টেইনার ইতিমধ্যে সেই হোস্ট পোর্ট প্রকাশ করেছে। docker ps দেখায় কোনটি; কয়েক সপ্তাহ আগের একটি পরীক্ষামূলক docker run থেকে অবশিষ্ট একটি পুরোনো কন্টেইনার সাধারণত এর জন্য দায়ী। যদি docker ps পরিষ্কার থাকে, তবে একটি non-Docker প্রসেস সেই পোর্ট ধরে রেখেছে: sudo ss -tlnp | grep 8080 সেটির নাম বলে।

yaml: line 14: did not find expected key — উল্লেখিত লাইনে বা তার ঠিক উপরে একটি ইন্ডেন্টেশন ত্রুটি। Compose ফাইলগুলি YAML: দুই-স্পেস ইন্ডেন্টেশন, শুধুমাত্র স্পেস, এবং যেকোনো জায়গায় একটি tab ক্যারেক্টার মারাত্মক। docker compose config কিছু শুরু না করেই ফাইলটি যাচাই করে, এবং প্রতিটি সম্পাদনার পরে এটি চালানো একটি সস্তা অভ্যাস।

The ufw surprise কোনো ত্রুটি মুদ্রণ করে না, এটিই এটিকে বিপজ্জনক করে তোলে: ডিপ্লয় কাজ করে, ufw status সঠিক দেখায়, এবং বাইরে থেকে একটি পোর্ট স্ক্যান তবুও আপনার ডেটাবেস খুঁজে পায়। উপরের পোর্ট বিভাগটি পুনরায় পড়ুন, প্রতিটি ports: এন্ট্রিতে একটি অনুপস্থিত 127.0.0.1: প্রিফিক্স আছে কিনা পরীক্ষা করুন, এবং একটি ভিন্ন মেশিন থেকে curl http://your-vps-ip:8080 দিয়ে নিশ্চিত করুন — connection refused হলো আপনার প্রত্যাশিত উত্তর।

এখান থেকে, Traefik গাইড এই একক স্ট্যাকটিকে একটি HTTPS এন্ট্রি পয়েন্টের পেছনে অনেকগুলি অ্যাপে পরিণত করে, এবং 2026 সালে স্ব-হোস্ট করার মতো কী কী হলো এটির মাধ্যমে চালানোর জন্য কেনাকাটার তালিকা।

VPS-এ একটি Minecraft সার্ভার-এর মতো একটি গেম সার্ভার হলো অনুশীলনের জন্য একটি সহজ প্রথম Compose প্রজেক্ট।

FAQ

আমি "permission denied while trying to connect to the Docker daemon socket" ত্রুটি কেন পাচ্ছি?

আপনার ব্যবহারকারী docker গ্রুপে নেই, অথবা বর্তমান সেশন শুরু হওয়ার পরে যোগ করা হয়েছে — সদস্যপদ কেবল লগইনের সময়ই কার্যকর হয়। sudo usermod -aG docker $USER চালান, তারপর newgrp docker চালান অথবা লগআউট করে আবার লগইন করুন, এবং id দিয়ে নিশ্চিত করুন। এই গ্রুপটি হোস্টে root-এর সমতুল্য অ্যাক্সেস দেয়, তাই শুধুমাত্র তাদেরই যোগ করুন যাদের আপনি sudo দেওয়ার কথা ভাবেন।

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

সাধারণ docker compose down মুছে দেয় না — এটি শুধু কন্টেইনার এবং প্রজেক্ট নেটওয়ার্ক সরিয়ে দেয়; নামযুক্ত ভলিউমগুলি থেকে যায় এবং পরবর্তী up -d সেগুলিকে পুনরায় সংযুক্ত করে। docker compose down -v হলো ধ্বংসাত্মক রূপ: এটি নামযুক্ত ভলিউমগুলি মুছে দেয়, অর্থাৎ আপনার ডেটাবেস, কোনো নিশ্চিতকরণ ছাড়াই এবং পূর্বাবস্থায় ফেরানোর উপায় ছাড়াই। যদি না আপনার কাছে যাচাইকৃত ব্যাকআপ থাকে, বাস্তব ডেটা সহ কোনো স্ট্যাকে -v চালাবেন না।

docker-compose এবং docker compose-এর মধ্যে পার্থক্য কী?

docker-compose (হাইফেন) হলো Compose v1, একটি স্বতন্ত্র Python বাইনারি যা 2023 সালে জীবনচক্রের শেষে পৌঁছেছে এবং নতুন সার্ভারে ইনস্টল করা উচিত নয়। docker compose (স্পেস) হলো Compose v2, Docker CLI-এর জন্য একটি Go প্লাগইন, যা Docker-এর apt রিপোজিটরি থেকে docker-compose-plugin হিসেবে ইনস্টল করা হয়। কমান্ড এবং YAML প্রায় সম্পূর্ণ সামঞ্জস্যপূর্ণ, তাই যখন কোনো পুরোনো টিউটোরিয়ালে docker-compose up লেখা থাকে, আপনি docker compose up টাইপ করুন।

ufw পোর্ট ব্লক করা সত্ত্বেও আমি কীভাবে ইন্টারনেট থেকে আমার Docker কন্টেইনারে পৌঁছাচ্ছি?

কারণ Docker iptables-এর PREROUTING চেইনে DNAT নিয়ম দিয়ে পোর্ট প্রকাশ করে, এবং পুনর্লিখিত প্যাকেটগুলি Docker-এর নিজস্ব চেইনের মধ্য দিয়ে FORWARD পথে চলাচল করে — সেগুলি কখনোই INPUT চেইনে পৌঁছায় না যেখানে ufw-এর নিয়ম প্রযোজ্য হয়। তাই ufw deny 8080 একটি প্রকাশিত কন্টেইনার পোর্টে কোনো প্রভাব ফেলে না। উৎসেই এটি ঠিক করুন: 127.0.0.1:-এ প্রকাশ করুন এবং পরিষেবাগুলি একটি রিভার্স প্রক্সির মাধ্যমে উন্মুক্ত করুন।

আমার কি নামযুক্ত ভলিউম ব্যবহার করা উচিত নাকি বাইন্ড মাউন্ট?

শুধুমাত্র কন্টেইনার যে ডেটা ব্যবহার করে সেটির জন্য নামযুক্ত ভলিউম ব্যবহার করুন — বিশেষ করে ডেটাবেসের জন্য, কারণ Docker ইমেজ যে মালিকানা আশা করে সেটি নির্ধারণ করে এবং অনুমতিগুলি সঠিকভাবে কাজ করে। হোস্ট থেকে আপনি যে ফাইলগুলি নিজেও পরিচালনা করেন সেগুলির জন্য বাইন্ড মাউন্ট ব্যবহার করুন: আপনি যে কনফিগ সম্পাদনা করেন, আপনি যে মিডিয়া আপলোড করেন, এমন কিছু যার পথ আপনি স্পষ্ট চান। কোনো কন্টেইনার যদি বাইন্ড মাউন্টে স্টার্টআপে permission denied ত্রুটি দিয়ে ব্যর্থ হয়, তবে হোস্ট-বনাম-কন্টেইনার UID অমিল প্রথম যে বিষয়টি যাচাই করা উচিত।