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

Docker Compose দিয়ে Planka সেলফ-হোস্ট করার নিয়ম

Docker Compose ব্যবহার করে নিজের সার্ভারে Planka সেটআপ করুন। Postgres ও Traefik কনফিগারেশন এবং BASE_URL সেটিংয়ের মাধ্যমে লগইন এরর এড়িয়ে চলার পূর্ণাঙ্গ গাইড এখানে দেওয়া হলো।

Planka self-hosting করে আপনি যা পাবেন

Planka self-hosting করলে আপনার টিম এমন একটি Kanban বোর্ড পাবে যেখানে কার্ড, লিস্ট এবং লেবেলের মডেলটি Trello-এর মতোই পরিচিত, যা আপনার নিজের নিয়ন্ত্রণাধীন একটি VPS-এ চলবে। এখানে কোনো সিট লিমিট বা প্রতি ব্যবহারকারীর জন্য বিলিং নেই, কারণ একমাত্র খরচ হলো সার্ভারের খরচ। এই নির্দেশিকাটি Traefik-এর পেছনে Docker Compose ব্যবহার করে এটি ডেপ্লয় করবে, যেখানে ডেটার জন্য Postgres এবং আপলোড করা প্রতিটি ফাইলের জন্য একটি named volume ব্যবহার করা হয়েছে।

আমি এমন একটি টিমের কথা মাথায় রেখেছি যারা দুই থেকে পাঁচ সদস্যের এবং Trello-এর ফ্রি টিয়ার ছেড়ে আসছে। আপনি যদি এখনো সিদ্ধান্ত না নিয়ে থাকেন যে কোন বোর্ডটি ব্যবহার করবেন, তবে প্রথমে self-hosted Trello বিকল্পগুলোর তুলনা পড়ুন। এই নির্দেশিকাটি ধরে নিচ্ছে যে আপনি সিদ্ধান্ত নিয়ে ফেলেছেন এবং এটি শুধুমাত্র ডেপ্লয়মেন্টের ওপর আলোকপাত করবে।

আপনার প্রয়োজন Docker Engine এবং Compose plugin সহ একটি VPS, এবং সেটির দিকে নির্দেশ করা একটি DNS A record। আপনার সেই বক্সে আগে থেকেই TLS (transport layer security) টার্মিনেট করছে এমন একটি Traefik ইনস্ট্যান্স থাকতে হবে। যদি Traefik এখনো সেটআপ করা না থাকে, তবে প্রথমে কয়েকটি Compose অ্যাপের সামনে একটি Traefik reverse proxy সেটআপ করুন, এবং নিচের ফাইলটি অপরিচিত মনে হলে Docker Compose-এর মৌলিক বিষয়গুলো পড়ুন।

Planka-এর জন্য কতটুকু VPS প্রয়োজন?

প্রজেক্টটি হার্ডওয়্যারের কোনো নির্দিষ্ট সর্বনিম্ন সীমা প্রকাশ করে না, তাই আপনি যে সংখ্যাই পড়ুন না কেন, সেটিকে একটি পরিমাপের বদলে শুরুর বিন্দু হিসেবে গণ্য করুন। হোস্টিং পেজগুলোতে যে 2 vCPU এবং 4 GB-এর কথা বলা হয়, তা মূলত প্রোভাইডারদের একটি সুবিধাজনক ডিফল্ট মান, প্রজেক্টের কোনো পরীক্ষিত প্রয়োজনীয়তা নয়। পাঁচজন ব্যবহারকারীর একটি বোর্ডের জন্য এটি বেশ উদার একটি কনফিগারেশন।

প্রকৃতপক্ষে যা চলে তা বেশ ছোট: API এবং বিল্ড করা frontend পরিবেশন করার জন্য একটি Node.js প্রসেস এবং ডেটা ধরে রাখার জন্য একটি Postgres প্রসেস। Planka কন্টেইনারের ভেতরে আউটগোয়িং রিকোয়েস্ট ফিল্টার করার জন্য একটি ছোট প্রক্সি প্রসেস চলে। 1 vCPU এবং 2 GB-এর একটি প্ল্যান দুই থেকে পাঁচজনের একটি বোর্ড অনায়াসেই চালাতে পারে এবং অবশিষ্ট মেমরির বেশিরভাগ অংশ Postgres ক্যাশ হিসেবে ব্যবহৃত হয়। একটি বোর্ড খুব হালকা একটি অ্যাপ্লিকেশন, তাই যদি একই VPS-এ আপনার টিমের ডকুমেন্ট রাখার পরিকল্পনা থাকে, তবে সেই অ্যাপটির জন্য আগে জায়গা নির্ধারণ করুন: Notion-এর বিকল্প হিসেবে AFFiNE চালানো-এর জন্য Planka-এর চাহিদার আগেই কয়েক গিগাবাইট মেমরির প্রয়োজন হয়।

মেমরি নির্ধারণের আগে ডিস্কের আকার নির্ধারণ করুন, কারণ অ্যাটাচমেন্টগুলোই সময়ের সাথে বাড়তে থাকে। এই অনুচ্ছেদের ওপর নির্ভর না করে আপনার নিজের ইনস্ট্যান্সটি পরিমাপ করুন:

docker stats --no-stream
docker system df -v

প্রথম কমান্ডটি প্রতিটি কন্টেইনারের লাইভ মেমরি এবং CPU ব্যবহার দেখায়। দ্বিতীয়টি প্রতিটি ভলিউম কতটুকু জায়গা দখল করে আছে তা দেখায়। এই পরিমাপগুলো ইনস্টল করার দিন না নিয়ে, একটি স্বাভাবিক কর্মসপ্তাহ পার হওয়ার পর নিন, কারণ একটি অব্যবহৃত বোর্ড আপনার টিমের কাজের ধরন সম্পর্কে কোনো তথ্য দেয় না।

Compose ফাইলটি লিখুন

ডিরেক্টরি তৈরি করুন এবং সেটির মালিকানা গ্রহণ করুন, যাতে আপনাকে আর কখনোই sudo ব্যবহার করে এই ফাইলগুলো এডিট করতে না হয়।

sudo mkdir -p /opt/planka
sudo chown "$USER":"$USER" /opt/planka
cd /opt/planka

Compose ফাইলের পাশে একটি .env ফাইলে সিক্রেটগুলো তৈরি করুন। Compose স্বয়ংক্রিয়ভাবে সেই ফাইলটি পড়ে এবং মানগুলো প্রতিস্থাপন করে।

umask 077
{
  printf 'SECRET_KEY=%s\n' "$(openssl rand -hex 64)"
  printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 24)"
  printf 'ADMIN_PASSWORD=%s\n' "$(openssl rand -hex 12)"
} > .env
chmod 600 .env

openssl rand -hex ব্যবহার করা হয়েছে ইচ্ছাকৃতভাবে। একটি হেক্স স্ট্রিং-এ শুধুমাত্র সংখ্যা এবং a থেকে f পর্যন্ত অক্ষর থাকে, তাই এটি যে DATABASE_URL কানেকশন স্ট্রিং-এ বসানো হয়, সেটিকে নষ্ট করতে পারে না। স্ল্যাশ বা অ্যাট সাইন (@) যুক্ত একটি base64 পাসওয়ার্ড এমন একটি কানেকশন এরর তৈরি করে যা দেখতে ভুল হোস্টনেমের মতো মনে হয়, এবং এটি সমাধান করতে আপনার এক ঘণ্টা সময় নষ্ট হতে পারে। এই বৃহত্তর বিষয়টি Compose ফাইল থেকে সিক্রেট দূরে রাখা অংশে আলোচনা করা হয়েছে।

এখন docker-compose.yml। যেখানে kanban.example.com দুইবার এসেছে, সেখানে আপনার নিজের হোস্টনেম দিয়ে প্রতিস্থাপন করুন।

services:
  planka:
    image: ghcr.io/plankanban/planka:2.1.1
    restart: unless-stopped
    volumes:
      - planka-data:/app/data
    environment:
      - BASE_URL=https://kanban.example.com
      - DATABASE_URL=postgresql://planka:${POSTGRES_PASSWORD}@postgres/planka
      - SECRET_KEY=${SECRET_KEY}
      - TRUST_PROXY=true
      - DEFAULT_ADMIN_EMAIL=you@example.com
      - DEFAULT_ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - DEFAULT_ADMIN_NAME=Your Name
      - DEFAULT_ADMIN_USERNAME=admin
    networks:
      - proxy
      - internal
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.planka.rule=Host(`kanban.example.com`)"
      - "traefik.http.routers.planka.entrypoints=websecure"
      - "traefik.http.routers.planka.tls.certresolver=default"
      - "traefik.http.services.planka.loadbalancer.server.port=1337"
    depends_on:
      postgres:
        condition: service_healthy

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

volumes:
  planka-data:
  db-data:

networks:
  proxy:
    external: true
  internal:

এই ফাইলের চারটি সিদ্ধান্ত ব্যাখ্যা করা প্রয়োজন, কারণ মানুষ সাধারণত এগুলো পরিবর্তন করে এবং পরে পস্তায়।

  • Planka সার্ভিসে কোনো ports: ব্লক নেই। Traefik কন্টেইনারটির সাথে proxy নেটওয়ার্কের মাধ্যমে যোগাযোগ করে, তাই 1337 পোর্টটি হোস্ট মেশিনে কখনোই পাবলিশ করা হয় না। এটি পাবলিশ করলে যে কেউ আপনার প্রক্সি এবং সার্টিফিকেটের সুরক্ষা এড়িয়ে সরাসরি প্রবেশ করতে পারবে।
  • loadbalancer.server.port=1337 কন্টেইনারের ভেতরের পোর্টটিকে নির্দেশ করে। Planka 1337 পোর্টে লিসেন করে, এবং আপস্ট্রিম উদাহরণে এটি 3000 পোর্টে পৌঁছায় কারণ তারা পোর্টটিকে হোস্টের সাথে ম্যাপ করে। এখানে কোনো হোস্ট ম্যাপিং নেই, তাই Traefik-কে কন্টেইনার পোর্টটি জানিয়ে দিতে হয়।
  • condition: service_healthy অংশটি Postgres হেলথচেকের সাথে কাজ করে। এটি ছাড়া Planka ডাটাবেস কানেকশন গ্রহণ করার আগেই চালু হয়ে যায়, প্রথম কুয়েরিতেই ব্যর্থ হয় এবং বন্ধ হয়ে যায়, যা দেখতে ক্র্যাশ লুপের মতো মনে হয়। এর কার্যপদ্ধতি Compose হেলথচেক এবং স্টার্টআপ অর্ডারিং অংশে দেওয়া আছে।
  • ডাটাবেস সার্ভিসটির নাম ইচ্ছাকৃতভাবে postgres রাখা হয়েছে। Planka 2 তার নিজস্ব আউটগোয়িং রিকোয়েস্টগুলোকে একটি ইন্টারনাল ফিল্টারের মাধ্যমে পাঠায় যার ডিফল্ট ব্লক লিস্ট হলো localhost,postgres। সার্ভিসটির নাম পরিবর্তন করলে আপনি নীরবে আপনার ডাটাবেসকে সেই লিস্ট থেকে সরিয়ে ফেলবেন।

কিছু শুরু করার আগে Compose আপনার সিক্রেটগুলো দেখতে পাচ্ছে কি না তা যাচাই করুন:

docker compose config | grep -E 'image:|BASE_URL|POSTGRES_USER'

এটি এমন একটি ফাইল প্রিন্ট করবে যেখানে .env মানগুলো আগে থেকেই প্রতিস্থাপিত আছে। যদি মান খালি দেখায়, তার মানে Compose .env ফাইলটি পড়তে পারছে না, সাধারণত এর কারণ হলো আপনি ভিন্ন কোনো ডিরেক্টরি থেকে কমান্ডটি চালাচ্ছেন।

অ্যাডমিন বুটস্ট্র্যাপ ভেরিয়েবলগুলো আসলে কী কাজ করে

Planka 1.13 ভার্সন থেকে আপনার জন্য স্বয়ংক্রিয়ভাবে কোনো অ্যাডমিনিস্ট্রেটর তৈরি করা হয় না, তাই একটি নতুন ডাটাবেসে লগইন করার মতো কেউ থাকে না। DEFAULT_ADMIN_* গ্রুপটি এই সমস্যার সমাধানের দুটি উপায়ের একটি।

স্টার্টআপের সময় Planka DEFAULT_ADMIN_EMAIL-এর সাথে মিলে এমন কোনো ব্যবহারকারী আছে কি না তা খুঁজে দেখে। যদি কেউ না থাকে, তবে এটি তার পাশে সেট করা পাসওয়ার্ড, ডিসপ্লে নেম এবং ইউজারনেম ব্যবহার করে একটি অ্যাকাউন্ট তৈরি করে। এটি একটি খালি ডাটাবেসে প্রথমবার বুট করার সময় ঘটে, তাই এই ভেরিয়েবলগুলো অ্যাকাউন্ট ম্যানেজ করার পরিবর্তে সেটিকে বুটস্ট্র্যাপ করে।

DEFAULT_ADMIN_EMAIL একটি দ্বিতীয় কাজ করে যা অনেককে বিভ্রান্ত করে। যতক্ষণ এই ভেরিয়েবলটি সেট করা থাকে, ততক্ষণ ইন্টারফেস থেকে অন্য কেউ এই অ্যাকাউন্টের নাম পরিবর্তন বা ডিলিট করতে পারে না। এটি একটি লক-আউট গার্ড, এবং এই কারণেই আপনি UI-তে সেই অ্যাকাউন্টের নাম বা ইমেইল অ্যাড্রেস পরিবর্তন করতে পারেন না। ভেরিয়েবলটি সরিয়ে ফেলুন এবং রিস্টার্ট করুন, তাহলে অ্যাকাউন্টটি সাধারণ অ্যাডমিনে পরিণত হবে যা আপনি অন্য যেকোনো অ্যাকাউন্টের মতো এডিট করতে পারবেন।

পাসওয়ার্ড লাইনটির ক্ষেত্রে সতর্ক থাকতে হবে। environment:-এর অধীনে থাকা যেকোনো কিছু যে কেউ দেখতে পারে যে কন্টেইনারে docker inspect চালাতে পারে, তাই DEFAULT_ADMIN_PASSWORD সেখানে স্থায়ীভাবে রাখা উচিত নয়। লগইন করুন, ইন্টারফেসে আপনার পাসওয়ার্ড পরিবর্তন করুন, সেই লাইনটি ডিলিট করুন, তারপর আবার docker compose up -d চালান।

পরিচ্ছন্ন পদ্ধতিটি হলো ভেরিয়েবলগুলো পুরোপুরি এড়িয়ে যাওয়া। পুরো DEFAULT_ADMIN_* গ্রুপটিকে কমেন্ট আউট করুন, তারপর ইন্টারঅ্যাক্টিভভাবে অ্যাকাউন্টটি তৈরি করুন:

docker compose run --rm planka npm run db:create-admin-user

এটি ইমেইল, পাসওয়ার্ড, ডিসপ্লে নেম এবং ঐচ্ছিক ইউজারনেম জানতে চাইবে এবং সরাসরি ডাটাবেসে ব্যবহারকারীর তথ্য লিখে রাখবে। পাসওয়ার্ডটি কখনোই Compose ফাইলে বা কন্টেইনার এনভায়রনমেন্টে থাকে না। যদি একাধিক ব্যক্তির VPS-এ শেল অ্যাক্সেস থাকে, তবে এই পদ্ধতিটি ব্যবহার করুন। depends_on-এর কারণে কমান্ডটি প্রথমে Postgres চালু করে, তাই এটি এমন একটি স্ট্যাকেও কাজ করে যা আগে কখনো চালু করা হয়নি।

উভয় পদ্ধতিই আপনাকে হাতে কলমে Planka পাসওয়ার্ড ম্যানেজ করার সুযোগ দেয়। যদি এটি আপনার টিমের সংগ্রহ করা চতুর্থ ক্রেডেনশিয়াল সেট হয়, তবে Planka লগইন প্রক্রিয়াটি কোনো OIDC প্রোভাইডারের কাছে অর্পণ করতে পারে, যেমন আপনার নিজস্ব সিঙ্গেল সাইন-অন সার্ভার হিসেবে চলমান Authentik। এক্ষেত্রে বুটস্ট্র্যাপ অ্যাডমিনকে একটি ব্রেক-গ্লাস অ্যাকাউন্ট হিসেবে রাখুন, যা প্রোভাইডার ডাউন থাকলে কাজে আসবে।

কেন BASE_URL সঠিক hostname-এর সাথে না মিললে লগইন ব্যর্থ হয়

BASE_URL হলো সেই সঠিক ঠিকানা যা ব্যবহারকারীরা ব্রাউজারে টাইপ করেন, যার মধ্যে scheme থাকে কিন্তু শেষে কোনো trailing slash থাকে না। এই stack-এর জন্য এটি হলো https://kanban.example.com। Planka এই মান থেকে নিজস্ব লিঙ্ক এবং WebSocket সংযোগ তৈরি করে, যার অর্থ হলো একটি ভুল BASE_URL আপনাকে কোনো স্পষ্ট error message দেবে না। এর পরিবর্তে আপনি এমন একটি পেজ পাবেন যা লোড হবে কিন্তু কখনোই লোড হওয়া শেষ হবে না।

সাধারণ সমস্যাটি হলো: আপনি upstream-এর উদাহরণটি কপি করেন, BASE_URL=http://localhost:3000-কে অপরিবর্তিত রাখেন এবং আপনার আসল domain-এ HTTPS-এর মাধ্যমে সাইটটিতে প্রবেশ করেন। লগইন ফর্মটি সাবমিট হয় এবং আপনার credentials গৃহীত হয়। কিন্তু বোর্ডটি কখনোই প্রদর্শিত হয় না। ব্রাউজারের developer console খুললে আপনি দেখতে পাবেন /socket.io/-এ করা অনুরোধগুলো ব্যর্থ হচ্ছে, কারণ client-কে তার live connection localhost:3000-এ খোলার নির্দেশ দেওয়া হয়েছিল, আর আপনার ল্যাপটপে সেই ঠিকানার কোনো অস্তিত্ব নেই।

TRUST_PROXY=true হলো একই সমস্যার অন্য অর্ধেক। Planka যেহেতু Traefik-এর পেছনে থাকে, তাই প্রতিটি অনুরোধ Docker network-এর ভেতর দিয়ে proxy-র ঠিকানা থেকে plain HTTP-তে পৌঁছায়। TRUST_PROXY ছাড়া, অ্যাপটি Traefik কর্তৃক সেট করা X-Forwarded-Proto এবং X-Forwarded-For header-গুলোকে উপেক্ষা করে। ফলে এটি মনে করে যে সংযোগটি অনিরাপদ এবং প্রতিটি client-কে একটি শেয়ার করা IP ঠিকানা হিসেবে গণ্য করে। এটি সেট করা থাকলে, অ্যাপটি সেই header-গুলো পড়ে এবং scheme-এর বিষয়ে browser-এর সাথে একমত হয়।

Traefik কোনো অতিরিক্ত configuration ছাড়াই WebSockets proxy করতে পারে, যা এখানে এটিকে পছন্দের একটি কারণ করে তোলে। nginx-এর ক্ষেত্রে, socket.io-এর জন্য নিজস্ব location block প্রয়োজন হয়, যেখানে proxy_set_header Upgrade $http_upgrade এবং proxy_set_header Connection "upgrade" থাকতে হয়, অন্যথায় ভিন্ন কারণে আপনি একই রকম আটকে থাকা spinner দেখতে পাবেন।

পরবর্তীতে বোর্ডটিকে নতুন কোনো hostname-এ স্থানান্তর করার অর্থ হলো দুটি বিষয় একসাথে পরিবর্তন করা: BASE_URL-এর মান এবং Traefik Host() rule। একটি পরিবর্তন করে অন্যটি ভুলে গেলে আপনি আবার সেই spinner-এর সমস্যায় পড়বেন। মার্চ 2026-এ রিলিজ হওয়া 2.1.0 সংস্করণ থেকে Planka-কে https://example.com/planka-এর মতো subpath থেকে চালানো সম্ভব। পুরনো tag-গুলোর ক্ষেত্রে, এটিকে নিজস্ব একটি subdomain দিন।

Planka যেখানে অ্যাটাচমেন্ট এবং অবতার সংরক্ষণ করে

Planka 2 ব্যবহারকারীর আপলোড করা সমস্ত ফাইল কন্টেইনারের ভেতরে একটি মাত্র পাথে সংরক্ষণ করে: /app/data। অ্যাটাচমেন্ট, ব্যবহারকারীর অবতার এবং বোর্ডের ব্যাকগ্রাউন্ড ইমেজ—সবই এই ডিরেক্টরির অধীনে থাকে। ভার্সন 1-এ তিনটি আলাদা ডিরেক্টরি ব্যবহৃত হতো, তাই পুরনো কোনো টিউটোরিয়াল থেকে কপি করা Compose ফাইলে এমন সব পাথ মাউন্ট করা থাকে যা এখন আর নেই, ফলে আসল ডেটা ডিরেক্টরিটি মাউন্ট করা হয় না।

এই একটি মাউন্ট পয়েন্টই নির্ধারণ করে আপনার বোর্ডটি আপগ্রেডের পর টিকে থাকবে নাকি আপনার বিকেলটি মাটি হবে। যদি /app/data কোনো ভলিউমে না থাকে, তবে আপলোডগুলো কন্টেইনারের রাইটেবল লেয়ারে জমা হয়। কন্টেইনার রিক্রিয়েট করার সময় সেই লেয়ারটি মুছে যায়, আর প্রতিবার ইমেজ ট্যাগ পরিবর্তন করলেই কন্টেইনার রিক্রিয়েট হয়। বোর্ডটি দেখতে ঠিকঠাক মনে হলেও এবং কার্ডগুলো সব থাকলেও, প্রতিটি অ্যাটাচমেন্ট লিঙ্ক অকেজো হয়ে যায়, কারণ ডেটাবেস রো-গুলো এমন ফাইলের দিকে নির্দেশ করে যা আর অস্তিত্বশীল নয়।

উপরের Compose ফাইলে থাকা নেমড ভলিউম এটি প্রতিরোধ করে। একটি বাইন্ড মাউন্টও কাজ করে এবং সাধারণ টুল দিয়ে ফাইলগুলোর ব্যাকআপ নেওয়া সহজ করে, তবে এর জন্য একটি অতিরিক্ত ধাপ প্রয়োজন। কন্টেইনারের ভেতরে থাকা Node প্রসেসটি UID 1000 হিসেবে চলে, তাই root-এর মালিকানাধীন কোনো হোস্ট ডিরেক্টরি ব্যবহার করলে প্রথম আপলোডের সময় পারমিশন এরর দেখা দেয়:

sudo chown -R 1000:1000 /opt/planka/data

এই দুটির মধ্যে কোনটি বেছে নেবেন তার সুবিধা-অসুবিধা bind mounts against named volumes-এ আলোচনা করা হয়েছে।

যদি আপনার প্ল্যানের ডিস্কের তুলনায় অ্যাটাচমেন্টের আকার বেড়ে যায়, তবে Planka সেগুলোকে S3-সামঞ্জস্যপূর্ণ স্টোরেজে সংরক্ষণ করতে পারে। এর জন্য S3_ENDPOINT, S3_BUCKET এবং সংশ্লিষ্ট কি ভেরিয়েবলগুলো ব্যবহার করতে হয়। এটি কোনো হোস্টেড বাকেটের দিকে অথবা অন্য কোনো মেশিনে থাকা a self-hosted MinIO object store-এর দিকে নির্দেশ করতে পারে। টিম বোর্ডে ফাইল আপলোড শুরু করার আগেই এই সিদ্ধান্ত নিন, কারণ এই সেটিংটি শুধুমাত্র নতুন আপলোডের ক্ষেত্রে কার্যকর হয়।

স্ট্যাকটি চালু করুন এবং এটি কাজ করছে কি না তা যাচাই করুন

docker compose pull
docker compose up -d
docker compose ps

docker compose ps কমান্ডটি চালালে postgres-কে healthy হিসেবে এবং planka-কে running হিসেবে দেখানো উচিত। যদি Planka বারবার রিস্টার্ট হতে থাকে, তবে অ্যাপের আগে ডাটাবেস কানেকশন পরীক্ষা করা উচিত।

docker compose logs -f planka

একটি সফল প্রথম বুট ডাটাবেস মাইগ্রেশন সম্পন্ন করে এবং সার্ভারটি 1337 পোর্টে লিসেন করছে বলে রিপোর্ট করে। লগের ওপর নির্ভর না করে সরাসরি Postgres-কে জিজ্ঞাসা করে নিশ্চিত হোন যে স্কিমাটি সঠিকভাবে তৈরি হয়েছে:

docker compose exec postgres psql -U planka -d planka -c '\dt'

টেবিলের তালিকায় যদি board এবং card থাকে, তবে এর অর্থ হলো মাইগ্রেশন সফল হয়েছে। "Did not find any relations" বার্তার অর্থ হলো Planka কানেক্ট হতে পারেনি, তাই আপনার .env ফাইলের POSTGRES_USER এবং POSTGRES_PASSWORD মানের সাথে DATABASE_URL-এর মান মিলিয়ে দেখুন।

এরপর আপনার নিজের মেশিন থেকে রুটটি পরীক্ষা করুন, VPS থেকে নয়:

curl -I https://kanban.example.com

HTTP/2 200-এর অর্থ হলো Traefik-এর কাছে একটি সার্টিফিকেট আছে এবং এটি কন্টেইনারে পৌঁছাতে পারছে। Traefik থেকে 404 এরর আসার অর্থ হলো রাউটার লেবেলগুলো মেলেনি, সাধারণত এর কারণ হলো কন্টেইনারটি proxy নেটওয়ার্কের সাথে যুক্ত নেই। এখন সাইটটি খুলুন এবং অ্যাডমিন অ্যাকাউন্ট দিয়ে লগ ইন করুন।

প্রতিটি ভার্সন আপগ্রেডের আগে একটি pg_dump নিন

আপনার বোর্ডের ডেটা দুটি আলাদা স্টোরে থাকে, তাই ব্যাকআপে উভয়ই অন্তর্ভুক্ত করতে হবে: Postgres ডেটাবেস এবং planka-data ভলিউম। স্ট্যাক চালু থাকা অবস্থাতেই ডেটাবেসটি ডাম্প করুন।

docker compose exec -T postgres pg_dump -U planka -d planka > "planka-db-$(date +%F).sql"

-T ব্যবহার করা ঐচ্ছিক নয়। এটি ছাড়া Compose একটি সিউডো-টার্মিনাল বরাদ্দ করে, এবং টার্মিনাল লেয়ার স্ট্রিমের লাইন এন্ডিংগুলো পরিবর্তন করে ফেলে। ফলে আপনি এমন একটি ডাম্প ফাইল পাবেন যা রিস্টোর করার সময় মাঝপথে ব্যর্থ হবে। এই ব্যর্থতা কয়েক সপ্তাহ পরে ধরা পড়ে, যা সবচেয়ে খারাপ সময়।

এরপর আপলোডগুলো ব্যাকআপ নিন। প্রথমে ভলিউমের আসল নামটি খুঁজে বের করুন, কারণ Compose প্রজেক্ট ডিরেক্টরির নাম দিয়ে সেটির প্রিফিক্স তৈরি করে।

docker volume ls | grep planka
docker run --rm -v planka_planka-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/planka-files-$(date +%F).tgz -C /data .

প্রজেক্টের রিপোজিটরিতে docker-backup.sh এবং docker-restore.sh ফাইলগুলোও থাকে এবং অফিসিয়াল ডকুমেন্টেশনে সেগুলোকে nightly cron job-এ রাখার পরামর্শ দেওয়া হয়েছে। যেকোনো পদ্ধতিই কার্যকর। তবে যে ব্যাকআপ আপনি কখনো রিস্টোর করে দেখেননি, তা গ্রহণযোগ্য নয়। তাই একটি স্ক্র্যাচ VPS-এ একবার রিস্টোর করে নিশ্চিত হোন যে আপনি লগইন করতে পারছেন এবং অ্যাটাচমেন্ট খুলতে পারছেন। আপলোড গ্রহণকারী প্রতিটি Compose অ্যাপেই এই দুটি স্টোর থাকে। তাই পরবর্তীতে যদি আপনি আপনার সাপোর্ট ডেস্কের সাথে একই বক্সে Chatwoot স্থাপন করেন, তবে এখানে তৈরি করা রুটিনটিই সামান্য ভলিউম নাম পরিবর্তনের মাধ্যমে ব্যবহার করতে পারবেন।

প্রতিটি ভার্সন পরিবর্তনের ঠিক আগে ডাম্পটি রান করুন। গত রাতের ব্যাকআপ এবং আপনি যে মাইগ্রেশনটি করতে যাচ্ছেন তার আগের ব্যাকআপ এক নয়।

ট্যাগ পিন করা এবং রিলিজ নোট পড়া

সেই ফাইলের উভয় ইমেজ ট্যাগই উদ্দেশ্যমূলকভাবে পিন করা হয়েছে।

ghcr.io/plankanban/planka:2.1.1 হলো একটি নির্দিষ্ট রিলিজ, যা আগস্ট 2026 পর্যন্ত বর্তমান। latest আপস্ট্রিম থেকে নতুন কিছু প্রকাশ করলেই পরিবর্তিত হয়, তাই একটি রুটিন docker compose pull এমন সময়ে স্কিমা মাইগ্রেশন নিয়ে আসতে পারে যখন আপনি তা চাননি। সেই সংখ্যা পরিবর্তন করার আগে রিলিজ নোট পড়ুন, কারণ সেখানেই ব্রেকিং চেঞ্জ এবং সিকিউরিটি ফিক্সের বর্ণনা থাকে। ভার্সন 2.0.3 একটি সিকিউরিটি রিলিজ হিসেবে প্রকাশিত হয়েছিল, যা এমন একটি বিষয় যা আপনি দুর্ঘটনাবশত গ্রহণ না করে বরং পড়ে নেওয়াটাই শ্রেয়। এখানে পিন করা সহজ কারণ আপস্ট্রিম ইমেজ প্রকাশ করে, আর যেখানে কোনো প্রজেক্ট ইমেজ দেয় না, সেখানে আপনাকে অতিরিক্ত সতর্ক থাকতে হবে, যেমন চেক-আউট করা গিট ট্যাগ থেকে বক্সে তৈরি করা openGym-এর ক্ষেত্রে।

postgres:16-alpine একটি বড় ভার্সনের সাথে পিন করা হয়েছে আরও গুরুত্বপূর্ণ কারণে। Postgres তার ডেটা ডিরেক্টরি এমন ফরম্যাটে লেখে যা তার মেজর ভার্সনের সাথে যুক্ত, এবং সার্ভার অন্য কোনো ভার্সন দিয়ে লেখা ডিরেক্টরি খুলতে অস্বীকার করে। যদি আপনি postgres:latest লেখেন এবং ট্যাগটি 17-তে পরিবর্তিত হয়, তবে কন্টেইনারটি স্টার্ট হবে না:

FATAL:  database files are incompatible with server
DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.

এতে কিছুই হারিয়ে যায় না, এবং রিস্টার্ট করলেও কোনো সমস্যার সমাধান হয় না। নতুন Postgres মেজর ভার্সনে যাওয়ার অর্থ হলো পুরোনো ভার্সন থেকে ডেটা ডাম্প করা এবং নতুন ভার্সনে একটি ফ্রেশ ডেটা ডিরেক্টরিতে তা রিস্টোর করা। এটি স্ট্যাক বন্ধ রেখে পরিকল্পিতভাবে করার কাজ, ইমেজ পুল করার কোনো পার্শ্বপ্রতিক্রিয়া নয়।

আপনি যদি নতুন করে শুরু না করে বিদ্যমান Planka 1.x ইনস্টলেশন আপগ্রেড করেন, তবে সেই আপগ্রেডের নিজস্ব পদ্ধতি প্রজেক্ট ডকুমেন্টেশনে দেওয়া আছে, এবং আগে থেকে ব্যাকআপ না নেওয়া থাকলে ভার্সন 1-এ ফিরে আসার কোনো উপায় নেই।

ব্যর্থতার ধরন এবং যে বার্তাগুলো আপনি দেখতে পাবেন

Planka লুপে রিস্টার্ট হচ্ছে এবং লগে ডাটাবেসের নাম দেখাচ্ছে। DATABASE_URL-এ থাকা ক্রেডেনশিয়ালগুলো Postgres এনভায়রনমেন্টের সাথে মিলছে না। মনে রাখবেন, POSTGRES_PASSWORD শুধুমাত্র তখনই কার্যকর হয় যখন ডাটা ডিরেক্টরি প্রথমবার ইনিশিয়ালাইজ করা হয়, তাই প্রথমবার ভুল বুট হওয়ার পর ভেরিয়েবল পরিবর্তন করলে কোনো কাজ হবে না। আপনাকে db-data ভলিউমটি মুছে ফেলে নতুন করে শুরু করতে হবে।

লগইন সফল হচ্ছে কিন্তু বোর্ড লোড হচ্ছে না। BASE_URL ব্রাউজার বারের ঠিকানার সাথে মিলছে না, অথবা TRUST_PROXY অনুপস্থিত। ব্রাউজার কনসোলে /socket.io/-এর দিকে ব্যর্থ অনুরোধগুলো দেখা যাচ্ছে।

অন্য সবকিছু কাজ করলেও ফাইল আপলোড ব্যর্থ হচ্ছে। বাইন্ড মাউন্টটি root-এর মালিকানাধীন। হোস্ট ডিরেক্টরিতে sudo chown -R 1000:1000 চালান এবং কন্টেইনারটি রিস্টার্ট করুন।

আপগ্রেডের পর অ্যাটাচমেন্টগুলো হারিয়ে গেছে। /app/data ভলিউমে ছিল না, তাই ফাইলগুলো কন্টেইনার লেয়ারে ছিল যা আপগ্রেডের সময় প্রতিস্থাপিত হয়েছে। ব্যাকআপ থেকে ফাইলগুলো পুনরুদ্ধার করুন, তারপর ইমেজ ট্যাগ পরিবর্তন করার আগে ভলিউমটি যুক্ত করুন।

Traefik 404 ত্রুটি দেখাচ্ছে। কন্টেইনারটি proxy নেটওয়ার্কে নেই, অথবা Host() রুলটি আপনার DNS রেকর্ডের সাথে মিলছে না। docker compose config সাবস্টিটিউশনের পর লেবেলগুলো দেখায়, যেখানে টাইপো বা ভুলগুলো ধরা পড়ে।

নোটিফিকেশন বা ওয়েবহুক আসছে না। Planka 2 তার আউটগোয়িং HTTP অনুরোধগুলো একটি অভ্যন্তরীণ ফিল্টারের মাধ্যমে পাঠায় এবং ডিফল্ট ব্লক লিস্টে localhost এবং postgres অন্তর্ভুক্ত থাকে। একই হোস্টের অন্য কোনো কন্টেইনারের উদ্দেশ্যে পাঠানো ওয়েবহুক ডিজাইনের কারণেই ব্লক হতে পারে। ফিল্টারটি সরিয়ে ফেলার পরিবর্তে OUTGOING_ALLOWED_HOSTS অ্যাডজাস্ট করুন।

একবার এটি চালু হয়ে গেলে এর অপারেশনাল লোড খুব কম থাকে। রিলিজ নোটগুলোর দিকে নজর রাখুন এবং প্রতিটি আপগ্রেডের আগে ডাটাবেসের ডাম্প নিন। restart: unless-stopped-এর কারণে রিবুট হলে স্ট্যাকটি স্বয়ংক্রিয়ভাবে ফিরে আসে, যদি Docker সার্ভিসটি বুট করার সময় এনাবল করা থাকে। রিবুটের পর যে কম্পোজ স্ট্যাকগুলো ফিরে আসে না সেগুলোর ক্ষেত্রে করণীয় এখানে আলোচনা করা হয়েছে।

FAQ

লগ ইন করার পর Planka কেন লোড হতে থাকে?

আপনার ক্রেডেনশিয়াল গৃহীত হলেও লাইভ কানেকশনটি সফল হয়নি। Planka তার WebSocket URL তৈরি করে BASE_URL থেকে। তাই যদি এই ভেরিয়েবলটিতে http://localhost:3000 থাকে কিন্তু আপনি https://kanban.example.com ঠিকানায় সাইটটি ভিজিট করেন, তবে ব্রাউজার এমন একটি ঠিকানায় সকেট খোলার চেষ্টা করে যা আপনার মেশিনে নেই। ডেভেলপার কনসোলে /socket.io/-এ ব্যর্থ অনুরোধগুলো দেখা যাবে। BASE_URL-এ কোনো ট্রেইলিং স্ল্যাশ ছাড়া সঠিক পাবলিক অ্যাড্রেসটি সেট করুন, এবং TRUST_PROXY=true যোগ করুন যাতে অ্যাপটি আপনার রিভার্স প্রক্সি থেকে আসা X-Forwarded-Proto হেডারটি গ্রহণ করে। এরপর docker compose up -d কমান্ডটি চালান।

আমি কীভাবে প্রথম Planka অ্যাডমিন ইউজার তৈরি করব?

1.13 ভার্সন থেকে স্বয়ংক্রিয়ভাবে কোনো অ্যাডমিনিস্ট্রেটর তৈরি হয় না। হয় DEFAULT_ADMIN_EMAIL-এর সাথে সংশ্লিষ্ট পাসওয়ার্ড, নাম এবং ইউজারনেম ভেরিয়েবলগুলো সেট করে স্ট্যাকটি স্টার্ট করুন, অথবা docker compose run --rm planka npm run db:create-admin-user চালিয়ে প্রম্পটগুলোর উত্তর দিন। শেয়ার্ড সার্ভারের ক্ষেত্রে ইন্টারঅ্যাক্টিভ কমান্ডটি বেশি নিরাপদ, কারণ এতে পাসওয়ার্ডটি কন্টেইনার এনভায়রনমেন্টে প্রবেশ করে না যেখানে docker inspect এটি পড়তে পারে। পরবর্তীতে DEFAULT_ADMIN_EMAIL সেট করে রাখলে সেই অ্যাকাউন্টটি ইন্টারফেস থেকে এডিট বা ডিলিট করা যায় না।

Planka কোথায় অ্যাটাচমেন্ট এবং অবতার জমা রাখে?

Planka 2-এ সমস্ত আপলোড করা ফাইল কন্টেইনারের ভেতরে /app/data ডিরেক্টরিতে থাকে, যার মধ্যে অ্যাটাচমেন্ট, ইউজারের অবতার এবং বোর্ডের ব্যাকগ্রাউন্ড অন্তর্ভুক্ত। এই পাথটিকে একটি নেমড ভলিউমে মাউন্ট করুন। যদি এটি মাউন্ট করা না থাকে, তবে ফাইলগুলো কন্টেইনারের রাইটেবল লেয়ারে থাকে এবং কন্টেইনার পুনরায় তৈরি হলে (যা প্রতিটি ইমেজ আপগ্রেডের সময় ঘটে) সেগুলো মুছে যায়। বাইন্ড মাউন্টও কাজ করে, তবে Node প্রসেসটি UID 1000 হিসেবে চলে, তাই হোস্ট ডিরেক্টরিতে sudo chown -R 1000:1000 কমান্ডটি চালান, অন্যথায় পারমিশন এররের কারণে আপলোড ব্যর্থ হবে।

একটি সেলফ-হোস্টেড Planka-এর জন্য কতটুকু RAM প্রয়োজন?

প্রজেক্টটি কোনো হার্ডওয়্যার সীমার কথা উল্লেখ করেনি। হোস্টিং পেজগুলোতে যে 2 vCPU এবং 4 GB-এর কথা বলা হয়, তা মূলত প্রোভাইডারের ডিফল্ট মান, কোনো পরিমাপ নয়; ছোট বোর্ডের জন্য এটি বেশ বেশি। একটি Node প্রসেস এবং একটি Postgres প্রসেস হলো পুরো ওয়ার্কলোড, তাই 1 vCPU এবং 2 GB প্ল্যানে দুই থেকে পাঁচজনের একটি টিম অনায়াসে কাজ করতে পারে। স্বাভাবিক এক সপ্তাহ ব্যবহারের পর docker stats --no-stream চালিয়ে আপনার নিজের ডেটা অনুযায়ী সাইজ নির্ধারণ করুন। মেমরির চেয়ে ডিস্কের দিকে বেশি নজর রাখুন, কারণ অ্যাটাচমেন্টের আকারই সময়ের সাথে বাড়ে।

ডেটা না হারিয়ে আমি কীভাবে Planka আপগ্রেড করব?

আপগ্রেড করার ঠিক আগে ডেটাবেস ডাম্প করুন এবং আপলোড ভলিউমটি আর্কাইভ করুন, গত রাতের শিডিউলের ওপর নির্ভর করবেন না। docker compose exec -T postgres pg_dump -U planka -d planka > planka-db.sql ব্যবহার করুন এবং -T বজায় রাখুন যাতে সিউডো-টার্মিনাল রিডাইরেক্ট করা আউটপুটকে নষ্ট না করে। যে ভার্সনগুলো আপনি স্কিপ করছেন তার রিলিজ নোটগুলো পড়ুন, ইমেজ ট্যাগটিকে latest-এর পরিবর্তে নির্দিষ্ট রিলিজ ভার্সনে পরিবর্তন করুন, তারপর docker compose pull এবং docker compose up -d চালান এবং মাইগ্রেশনের জন্য লগ পর্যবেক্ষণ করুন। Postgres ট্যাগটিকে তার মেজর ভার্সনে পিন করে রাখুন, কারণ সার্ভার ভিন্ন মেজর ভার্সন দ্বারা লেখা ডেটা ডিরেক্টরি খুলতে অস্বীকার করে।