SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-28

آموزش نصب Planka با Docker Compose روی سرور شخصی

راهنمای کامل نصب Planka روی VPS با استفاده از Docker Compose و Traefik. یاد بگیرید چگونه Postgres را پیکربندی کنید و با تنظیم دقیق BASE_URL از خطای لاگین جلوگیری کنید.

مزایای میزبانی شخصی Planka

میزبانی شخصی Planka به تیم شما یک تخته کانبان با مدل کارت، لیست و برچسب می‌دهد که کاربران از Trello با آن آشنا هستند و روی یک VPS تحت کنترل شما اجرا می‌شود. هیچ محدودیتی در تعداد کاربران (seat) و هیچ هزینه ماهانه‌ای به ازای هر کاربر وجود ندارد، زیرا تنها هزینه شما همان سرور است. این راهنما Planka را با استفاده از Docker Compose پشت Traefik مستقر می‌کند، از Postgres برای داده‌ها استفاده کرده و برای هر فایلی که کاربران آپلود می‌کنند، یک named volume اختصاص می‌دهد.

مخاطب این راهنما تیمی دو تا پنج نفره است که قصد دارند از پلن رایگان Trello مهاجرت کنند. اگر هنوز در حال تصمیم‌گیری برای انتخاب ابزار مدیریت پروژه هستید، ابتدا مقایسه جایگزین‌های خودمیزبان Trello را مطالعه کنید. این راهنما فرض را بر این می‌گذارد که انتخاب نهایی انجام شده و تنها به مراحل استقرار می‌پردازد.

شما به یک VPS نیاز دارید که Docker Engine با پلاگین Compose روی آن نصب شده باشد و یک رکورد DNS از نوع A که به آن اشاره می‌کند. همچنین باید یک نمونه Traefik داشته باشید که در حال حاضر TLS (امنیت لایه انتقال) را روی همان سرور مدیریت می‌کند. اگر Traefik هنوز راه‌اندازی نشده است، ابتدا یک reverse proxy از نوع Traefik برای چندین برنامه Compose ایجاد کنید و اگر فایل زیر برایتان ناآشنا است، اصول Docker Compose برای VPS را مطالعه کنید.

Planka به چه میزان منابع VPS نیاز دارد؟

این پروژه حداقل سخت‌افزار رسمی منتشر نکرده است، بنابراین هر عددی که می‌خوانید را به عنوان یک نقطه شروع در نظر بگیرید، نه یک معیار دقیق. عدد 2 vCPU و 4 GB رم که در صفحات میزبانی تکرار می‌شود، یک مقدار پیش‌فرضِ راحت برای ارائه‌دهندگان است، نه الزامی که توسط خود پروژه اندازه‌گیری شده باشد. این مقدار برای بردی که 5 نفر با آن کار می‌کنند، بسیار سخاوتمندانه است.

آنچه در واقع اجرا می‌شود کوچک است: یک پردازش Node.js که API و فرانت‌اندِ build شده را سرویس می‌دهد، و یک پردازش Postgres که داده‌ها را نگه می‌دارد. یک پردازش پروکسی کوچک سوم نیز درون کانتینر Planka اجرا می‌شود تا درخواست‌های خروجی را فیلتر کند. یک پلن با 1 vCPU و 2 GB رم برای بردی با 2 تا 5 نفر کافی است و بخش عمده‌ای از حافظه آزاد به عنوان کش Postgres استفاده خواهد شد. یک برد، همسایه سبکی است؛ بنابراین اگر قرار است همان VPS میزبان اسناد تیم شما نیز باشد، ابتدا برای آن برنامه ظرفیت‌سنجی کنید: اجرای AFFiNE به عنوان یک فضای کاری مشابه Notion به تنهایی پیش از آنکه Planka درخواستی داشته باشد، به چند گیگابایت رم نیاز دارد.

پیش از تعیین اندازه حافظه، اندازه دیسک را تعیین کنید، زیرا پیوست‌ها بخشی هستند که رشد می‌کنند. به جای اعتماد به این پاراگراف، نمونه خود را اندازه‌گیری کنید:

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

دستور اول، میزان لحظه‌ای مصرف حافظه و CPU هر کانتینر را چاپ می‌کند. دستور دوم نشان می‌دهد هر volume چه مقدار فضا اشغال کرده است. هر دو اندازه‌گیری را پس از یک هفته کاری عادی انجام دهید، نه در روز نصب؛ زیرا یک بردِ بدون فعالیت، هیچ اطلاعاتی درباره تیم شما نمی‌دهد.

نوشتن فایل Compose

دایرکتوری را ایجاد کرده و مالکیت آن را در اختیار بگیرید تا دیگر نیازی نباشد این فایل‌ها را از طریق sudo ویرایش کنید.

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

رازها (secrets) را در یک فایل .env در کنار فایل Compose تولید کنید. 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 که دارای اسلش یا علامت at باشد، خطای اتصالی ایجاد می‌کند که شبیه به نام میزبان اشتباه به نظر می‌رسد و این موضوع یک ساعت از وقت شما را تلف خواهد کرد. الگوی کلی‌تر در نگهداری رازها خارج از فایل 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:

چهار تصمیم در آن فایل ارزش توضیح دارند، زیرا همان مواردی هستند که افراد تغییر می‌دهند و بعد پشیمان می‌شوند.

  • هیچ بلوک ports: روی سرویس Planka وجود ندارد. Traefik از طریق شبکه proxy به کانتینر دسترسی پیدا می‌کند، بنابراین پورت 1337 هرگز روی میزبان منتشر (publish) نمی‌شود. انتشار آن به هر کسی راهی برای دور زدن پروکسی و گواهی شما می‌دهد.
  • loadbalancer.server.port=1337 پورت داخل کانتینر را نام‌گذاری می‌کند. Planka روی 1337 گوش می‌دهد و نمونه بالادستی (upstream) فقط روی 3000 به آن می‌رسد زیرا پورت را به میزبان نگاشت (map) می‌کند. در اینجا هیچ نگاشت میزبانی وجود ندارد، بنابراین باید پورت کانتینر به Traefik اعلام شود.
  • condition: service_healthy با بررسی سلامت (healthcheck) Postgres جفت می‌شود. بدون آن، Planka پیش از آنکه پایگاه داده اتصالات را بپذیرد شروع به کار می‌کند، در اولین پرس‌وجوی خود شکست می‌خورد و خارج می‌شود که شبیه به یک حلقه خرابی (crash loop) به نظر می‌رسد. جزئیات فنی در بررسی‌های سلامت و ترتیب راه‌اندازی در Compose آمده است.
  • سرویس پایگاه داده به‌عمد postgres نام‌گذاری شده است. Planka 2 درخواست‌های خروجی خود را از طریق یک فیلتر داخلی هدایت می‌کند که لیست مسدودسازی پیش‌فرض آن localhost,postgres است. اگر سرویس را تغییر نام دهید، بی‌سروصدا پایگاه داده خود را از آن لیست حذف می‌کنید.

پیش از شروع هر کاری، بررسی کنید که Compose می‌تواند رازهای شما را ببیند:

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

این دستور فایل را با مقادیر .env که از قبل جایگزین شده‌اند، چاپ می‌کند. مقدار خالی به این معنی است که Compose فایل .env را نمی‌خواند، که معمولاً به این دلیل است که دستور را از دایرکتوری متفاوتی اجرا می‌کنید.

عملکرد واقعی متغیرهای bootstrap مدیر

از نسخه 1.13 به بعد Planka، هیچ حساب کاربری مدیری به‌صورت خودکار ایجاد نمی‌شود؛ بنابراین در یک پایگاه داده تازه، هیچ کاربری برای ورود وجود ندارد. گروه DEFAULT_ADMIN_* یکی از دو روش موجود برای رفع این مشکل است.

در زمان راه‌اندازی، Planka به دنبال کاربری می‌گردد که با DEFAULT_ADMIN_EMAIL مطابقت داشته باشد. اگر چنین کاربری موجود نباشد، Planka با استفاده از رمز عبور، نام نمایشی و نام کاربری تعیین‌شده در کنار آن، یک حساب ایجاد می‌کند. این اتفاق تنها در اولین بوت و در مواجهه با یک پایگاه داده خالی رخ می‌دهد؛ بنابراین این متغیرها برای bootstrap کردن یک حساب هستند، نه مدیریت آن.

DEFAULT_ADMIN_EMAIL وظیفه دومی هم دارد که اغلب باعث سردرگمی کاربران می‌شود. تا زمانی که این متغیر تنظیم شده باشد، حساب کاربری نام‌برده‌شده در آن، از طریق رابط کاربری توسط هیچ‌کس قابل ویرایش یا حذف نیست. این یک مکانیزم محافظتی برای جلوگیری از قفل شدن است و به همین دلیل است که نمی‌توانید نام آن حساب یا آدرس ایمیل آن را در UI تغییر دهید. برای رفع این محدودیت، متغیر را حذف کرده و سرویس را restart کنید تا حساب به یک مدیر معمولی تبدیل شود که مانند سایر حساب‌ها قابل ویرایش است.

در مورد خط رمز عبور باید دقت کافی داشته باشید. هر چیزی که تحت environment: قرار بگیرد، توسط هر کسی که بتواند docker inspect را روی container اجرا کند قابل خواندن است؛ بنابراین DEFAULT_ADMIN_PASSWORD نباید به‌صورت دائمی در آنجا باقی بماند. وارد سیستم شوید، رمز عبور خود را در رابط کاربری تغییر دهید، آن خط را حذف کنید و سپس دوباره docker compose up -d را اجرا کنید.

روش تمیزتر، صرف‌نظر کردن کامل از این متغیرها است. کل گروه DEFAULT_ADMIN_* را comment کنید و سپس حساب کاربری را به‌صورت تعاملی ایجاد کنید:

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

این دستور از شما ایمیل، رمز عبور، نام نمایشی و یک نام کاربری اختیاری می‌خواهد و کاربر را مستقیماً در پایگاه داده ثبت می‌کند. رمز عبور هرگز در فایل Compose یا محیط container قرار نمی‌گیرد. اگر بیش از یک نفر به shell سرور VPS دسترسی دارد، از این روش استفاده کنید. این دستور به دلیل depends_on ابتدا Postgres را اجرا می‌کند، بنابراین روی stackهایی که هرگز بالا نیامده‌اند نیز کار می‌کند.

هر دو روش، مدیریت رمزهای عبور Planka را به عهده خودتان می‌گذارند. اگر این چهارمین مجموعه از اعتبارنامه‌هایی است که تیم شما جمع‌آوری کرده، Planka می‌تواند احراز هویت را به یک ارائه‌دهنده OIDC مانند Authentik که به‌عنوان سرور single sign-on شخصی شما اجرا می‌شود واگذار کند؛ در این حالت، مدیر bootstrap به‌عنوان یک حساب اضطراری (break-glass) برای روزهایی که ارائه‌دهنده اصلی از دسترس خارج است، حفظ می‌شود.

چرا BASE_URL در صورت عدم تطابق با نام میزبان، باعث اختلال در ورود می‌شود

BASE_URL همان آدرس دقیقی است که کاربران در مرورگر وارد می‌کنند، شامل طرح (scheme) و بدون اسلش در انتها. برای این پشته، این مقدار https://kanban.example.com است. Planka لینک‌های خود و اتصال WebSocket را بر اساس این مقدار می‌سازد؛ بنابراین یک BASE_URL اشتباه، خطای واضحی نمی‌دهد. در عوض، صفحه‌ای دریافت می‌کنید که بارگذاری می‌شود اما هرگز تکمیل نمی‌شود.

نسخه رایج این مشکل: شما نمونه اولیه (upstream) را کپی می‌کنید، BASE_URL=http://localhost:3000 را به همان صورت باقی می‌گذارید و از طریق HTTPS به دامنه واقعی خود متصل می‌شوید. فرم ورود ارسال شده و اعتبارنامه شما پذیرفته می‌شود، اما تخته (board) هرگز نمایش داده نمی‌شود. کنسول توسعه‌دهنده مرورگر را باز کنید؛ خواهید دید که درخواست‌ها به /socket.io/ با شکست مواجه می‌شوند، زیرا به کلاینت گفته شده است که اتصال زنده خود را به localhost:3000 برقرار کند، در حالی که روی لپ‌تاپ شما چنین آدرسی وجود ندارد.

TRUST_PROXY=true نیمه دیگر همین مشکل است. Planka پشت Traefik قرار دارد، بنابراین هر درخواست از طریق آدرس پروکسی و با پروتکل HTTP ساده در شبکه Docker به آن می‌رسد. بدون TRUST_PROXY، برنامه هدرهای X-Forwarded-Proto و X-Forwarded-For که توسط Traefik تنظیم شده‌اند را نادیده می‌گیرد؛ در نتیجه تصور می‌کند اتصال ناامن است و با تمام کلاینت‌ها مانند یک آدرس IP مشترک رفتار می‌کند. با تنظیم این مقدار، برنامه هدرها را می‌خواند و در مورد طرح اتصال با مرورگر هم‌نظر می‌شود.

Traefik اتصالات WebSocket را بدون نیاز به پیکربندی اضافی پروکسی می‌کند، که یکی از دلایل ترجیح آن در اینجا است. در nginx، سرویس socket.io به بلاک location اختصاصی خود نیاز دارد که شامل proxy_set_header Upgrade $http_upgrade و proxy_set_header Connection "upgrade" باشد، در غیر این صورت با همان مشکل توقف بارگذاری (spinner) اما به دلیلی متفاوت مواجه خواهید شد.

انتقال تخته به یک نام میزبان جدید در آینده، مستلزم تغییر همزمان دو مورد است: مقدار BASE_URL و قانون Host() در Traefik. اگر یکی را تغییر دهید و دیگری را فراموش کنید، دوباره با مشکل توقف بارگذاری مواجه می‌شوید. ارائه Planka از یک زیرمسیر (subpath) مانند https://example.com/planka از نسخه 2.1.0 به بعد که در مارس 2026 منتشر شد، امکان‌پذیر است. در تگ‌های قدیمی‌تر، از یک زیردامنه اختصاصی برای آن استفاده کنید.

محل ذخیره‌سازی پیوست‌ها و آواتارها در Planka

نسخه 2 نرم‌افزار Planka تمام فایل‌های بارگذاری‌شده توسط کاربر را در یک مسیر واحد درون کانتینر ذخیره می‌کند: /app/data. پیوست‌ها، آواتارهای کاربران و تصاویر پس‌زمینه بردها همگی در این مسیر قرار دارند. در نسخه 1 از سه دایرکتوری مجزا استفاده می‌شد؛ بنابراین اگر فایل Compose را از آموزش‌های قدیمی کپی کرده باشید، مسیرهایی را mount می‌کنید که دیگر وجود ندارند و دایرکتوری اصلی داده‌ها بدون mount باقی می‌ماند.

آن یک mount واحد، تفاوت میان بردی است که پس از ارتقا سالم می‌ماند و بردی که باعث دردسر می‌شود. اگر /app/data روی یک volume قرار نداشته باشد، فایل‌های بارگذاری‌شده در لایه قابل‌نوشتن (writable layer) کانتینر ذخیره می‌شوند. این لایه با هر بار بازسازی کانتینر از بین می‌رود و کانتینر نیز با هر بار تغییر تگ image بازسازی می‌شود. در این حالت، برد پس از بالا آمدن ظاهر درستی دارد و کارت‌ها سر جایشان هستند، اما تمام لینک‌های پیوست از کار می‌افتند؛ زیرا ردیف‌های دیتابیس همچنان به فایل‌هایی اشاره می‌کنند که دیگر وجود ندارند.

استفاده از named volume در فایل Compose بالا از این اتفاق جلوگیری می‌کند. bind mount نیز کارآمد است و پشتیبان‌گیری از فایل‌ها با ابزارهای معمولی را ساده‌تر می‌کند، اما به یک مرحله اضافی نیاز دارد. پردازش Node درون کانتینر با UID 1000 اجرا می‌شود، بنابراین اگر مالکیت دایرکتوری روی میزبان (host) متعلق به root باشد، در اولین بارگذاری با خطای مجوز مواجه خواهید شد:

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

مزایا و معایب این دو روش در bind mounts در برابر named volumes بررسی شده است.

اگر حجم پیوست‌ها از فضای دیسک پلن شما فراتر رفت، Planka می‌تواند آن‌ها را از طریق S3_ENDPOINT، S3_BUCKET و متغیرهای کلیدی مربوطه، در فضای ذخیره‌سازی سازگار با S3 بنویسد. این تنظیمات می‌توانند به یک bucket میزبانی‌شده یا یک سرویس ذخیره‌سازی شیء MinIO خودمیزبان روی سروری دیگر اشاره کنند. پیش از آنکه تیم، برد را با داده پر کند در این مورد تصمیم بگیرید، زیرا این تنظیمات فقط برای بارگذاری‌های جدید اعمال می‌شوند.

راه‌اندازی پشته و بررسی صحت عملکرد آن

docker compose pull
docker compose up -d
docker compose ps

docker compose ps باید postgres را به عنوان healthy و planka را به عنوان running نشان دهد. اگر Planka در یک حلقه بازراه‌اندازی (restart loop) گیر کرده است، اولین چیزی که باید بررسی کنید اتصال پایگاه‌داده است، نه خود برنامه.

docker compose logs -f planka

یک بوت اولیه سالم، مهاجرت‌های (migrations) پایگاه‌داده را اجرا کرده و سپس گزارش می‌دهد که سرور روی پورت 1337 در حال گوش دادن است. برای اطمینان از اینکه طرحواره (schema) واقعاً ایجاد شده است، به‌جای اعتماد به لاگ، مستقیماً از Postgres پرس‌وجو کنید:

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

لیستی از جداول که شامل board و card باشد، به این معنی است که مهاجرت‌ها با موفقیت اجرا شده‌اند. پیام "Did not find any relations" به این معنی است که Planka هرگز متصل نشده است؛ بنابراین DATABASE_URL را با مقادیر POSTGRES_USER و POSTGRES_PASSWORD در فایل .env خود مقایسه کنید.

سپس مسیر دسترسی را از ماشین خودتان بررسی کنید، نه از داخل VPS:

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

HTTP/2 200 به این معنی است که Traefik گواهی را در اختیار دارد و به کانتینر دسترسی پیدا کرده است. خطای 404 که توسط Traefik ارسال می‌شود، به این معنی است که برچسب‌های (labels) مسیریاب مطابقت ندارند؛ این مشکل معمولاً به این دلیل رخ می‌دهد که کانتینر به شبکه proxy متصل نشده است. اکنون سایت را باز کرده و با حساب کاربری مدیر وارد شوید.

پیش از هر ارتقای نسخه، یک pg_dump بگیرید

دو فضای ذخیره‌سازی مجزا، داده‌های برنامه شما را نگه می‌دارند، بنابراین پشتیبان‌گیری باید هر دو را پوشش دهد: پایگاه داده Postgres و volume مربوط به planka-data. در حالی که stack در حال اجراست، از پایگاه داده dump بگیرید.

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

استفاده از -T اختیاری نیست. بدون آن، Compose یک pseudo-terminal اختصاص می‌دهد و لایه ترمینال، پایانه‌های خط را در جریان داده بازنویسی می‌کند؛ در نتیجه فایل dump حاصل در حین بازیابی (restore) با خطا مواجه می‌شود. این خطا ممکن است هفته‌ها بعد بروز کند که بدترین زمان ممکن برای مواجهه با آن است.

سپس نوبت به فایل‌های آپلود شده می‌رسد. ابتدا نام واقعی volume را پیدا کنید، زیرا 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 را در مخزن خود ارائه می‌دهد و مستندات رسمی، اجرای آن‌ها را در یک cron job شبانه توصیه می‌کنند. هر دو روش مناسب هستند. آنچه مناسب نیست، داشتن پشتیبانی است که هرگز آن را بازیابی نکرده‌اید؛ بنابراین یک بار آن را روی یک VPS آزمایشی بازیابی کنید و مطمئن شوید که می‌توانید وارد سیستم شوید و یک فایل پیوست را باز کنید. همین دو فضای ذخیره‌سازی در تمام برنامه‌های Compose که آپلود می‌پذیرند وجود دارند، بنابراین اگر بعداً Chatwoot را روی همان سرور پشتیبانی خود نصب کردید، روالی که اینجا ایجاد می‌کنید با تغییر نام volumeها قابل استفاده خواهد بود.

بلافاصله پیش از هر تغییر نسخه، dump بگیرید. پشتیبانی که دیشب گرفته شده با پشتیبانی که پیش از مهاجرتی که قصد انجام آن را دارید گرفته شده، متفاوت است.

تگ‌ها را ثابت (Pin) کنید و یادداشت‌های انتشار را بخوانید

هر دو تگ image در آن فایل به‌صورت عمدی ثابت شده‌اند.

ghcr.io/plankanban/planka:2.1.1 یک انتشار مشخص است که تا اوت 2026 معتبر است. latest با هر انتشار جدید توسط توسعه‌دهنده تغییر می‌کند، بنابراین یک docker compose pull روتین می‌تواند باعث شود که یک migration طرح‌واره (schema) در زمانی که شما انتخاب نکرده‌اید، اعمال شود. پیش از تغییر آن عدد، یادداشت‌های انتشار را بخوانید، زیرا تغییرات ساختارشکن (breaking changes) و اصلاحات امنیتی در آنجا شرح داده شده‌اند. نسخه 2.0.3 به‌عنوان یک انتشار امنیتی منتشر شد؛ این دقیقاً همان موردی است که می‌خواهید پیش از مواجهه ناگهانی با آن، درباره‌اش بدانید. ثابت کردن تگ‌ها در اینجا ساده است زیرا توسعه‌دهنده اصلی imageها را منتشر می‌کند، اما در پروژه‌هایی که هیچ image رسمی ارائه نمی‌دهند، شما باید همین نظم را با یک گام اضافه رعایت کنید، همان‌طور که در openGym که روی سیستم از یک git tag بررسی‌شده ساخته شده آمده است.

postgres:16-alpine به دلیل مهم‌تری روی یک نسخه اصلی (major version) ثابت شده است. Postgres دایرکتوری داده‌های خود را با فرمتی می‌نویسد که به نسخه اصلی وابسته است و سرور از باز کردن دایرکتوری که توسط نسخه دیگری نوشته شده، خودداری می‌کند. اگر postgres:latest را بنویسید و اجازه دهید تگ به 17 تغییر کند، container اجرا نخواهد شد:

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.

هیچ داده‌ای از دست نمی‌رود و با restart کردن هم چیزی درست نمی‌شود. انتقال به یک نسخه اصلی جدید Postgres به معنای گرفتن dump از نسخه قدیمی و restore کردن آن در یک دایرکتوری داده تازه در نسخه جدید است. این یک کار برنامه‌ریزی‌شده است که باید در زمان خاموش بودن stack انجام شود، نه یک اثر جانبی ناشی از pull کردن یک image.

اگر به‌جای شروع از صفر، قصد انتقال یک نصب موجود از Planka 1.x را دارید، این ارتقا رویه مستند خاص خود را در مستندات پروژه دارد و بدون داشتن نسخه پشتیبان (backup) که از قبل تهیه شده باشد، راهی برای بازگشت به نسخه 1 وجود ندارد.

حالت‌های شکست و پیام‌هایی که مشاهده خواهید کرد

Planka در یک حلقه مدام ری‌استارت می‌شود و لاگ به پایگاه داده اشاره دارد. اعتبارنامه‌ها در DATABASE_URL با متغیرهای محیطی Postgres مطابقت ندارند. توجه داشته باشید که POSTGRES_PASSWORD فقط هنگام اولین مقداردهی اولیه دایرکتوری داده اعمال می‌شود، بنابراین اصلاح متغیر پس از یک بوت ناموفق اولیه، تغییری ایجاد نمی‌کند. باید volume مربوط به db-data را حذف کرده و دوباره شروع کنید.

ورود موفقیت‌آمیز است اما برد (board) هرگز بارگذاری نمی‌شود. مقدار BASE_URL با آدرس موجود در نوار مرورگر مطابقت ندارد یا TRUST_PROXY تنظیم نشده است. کنسول مرورگر درخواست‌های ناموفق به /socket.io/ را نشان می‌دهد.

آپلودها شکست می‌خورند در حالی که سایر بخش‌ها کار می‌کنند. یک bind mount توسط root مالکیت شده است. دستور sudo chown -R 1000:1000 را روی دایرکتوری میزبان اجرا کرده و کانتینر را ری‌استارت کنید.

فایل‌های پیوست پس از ارتقا ناپدید شده‌اند. مسیر /app/data روی یک volume قرار نداشته است، بنابراین فایل‌ها در لایه کانتینر باقی مانده بودند که با ارتقا جایگزین شد. فایل‌ها را از نسخه پشتیبان بازیابی کنید و پیش از تغییر مجدد تگ ایمیج، volume را اضافه کنید.

سرویس Traefik خطای 404 برمی‌گرداند. کانتینر در شبکه proxy قرار ندارد یا قانون Host() با رکورد DNS شما مطابقت ندارد. دستور docker compose config برچسب‌ها را پس از جایگزینی نشان می‌دهد، جایی که غلط‌های تایپی آشکار می‌شوند.

اعلان‌ها یا وب‌هوک‌ها هرگز نمی‌رسند. نسخه 2 از Planka درخواست‌های HTTP خروجی خود را از طریق یک فیلتر داخلی ارسال می‌کند و لیست مسدودسازی پیش‌فرض شامل localhost و postgres است. یک وب‌هوک که به کانتینر دیگری روی همان میزبان اشاره دارد، ممکن است طبق طراحی مسدود شود. به جای حذف فیلتر، OUTGOING_ALLOWED_HOSTS را تنظیم کنید.

هنگامی که سرویس اجرا شد، بار عملیاتی آن اندک است. یادداشت‌های انتشار (release notes) را دنبال کنید و پیش از هر ارتقا، از پایگاه داده dump بگیرید. یک ری‌بوت باعث می‌شود stack به دلیل restart: unless-stopped به‌طور خودکار بالا بیاید، به شرطی که سرویس Docker در هنگام بوت فعال باشد؛ مطلب استک‌های Compose که پس از ری‌بوت بالا می‌آیند مواردی را که این اتفاق نمی‌افتد پوشش می‌دهد.

FAQ

چرا Planka پس از ورود به سیستم، بی‌نهایت در حال بارگذاری می‌ماند؟

اعتبارنامه‌ها پذیرفته شده‌اند اما اتصال زنده (live connection) برقرار نشده است. Planka آدرس WebSocket خود را از BASE_URL می‌سازد؛ بنابراین اگر این متغیر همچنان http://localhost:3000 باشد در حالی که شما از طریق https://kanban.example.com به سایت دسترسی دارید، مرورگر سعی می‌کند سوکتی به آدرسی باز کند که روی ماشین شما وجود ندارد. کنسول توسعه‌دهنده (developer console) درخواست‌های ناموفق به /socket.io/ را نشان می‌دهد. مقدار BASE_URL را دقیقاً روی آدرس عمومی بدون اسلش انتهایی تنظیم کنید، TRUST_PROXY=true را اضافه کنید تا برنامه هدر X-Forwarded-Proto را از reverse proxy شما بپذیرد، سپس docker compose up -d را اجرا کنید.

چگونه اولین کاربر مدیر (admin) را در Planka ایجاد کنم؟

از نسخه 1.13 به بعد، هیچ مدیر به‌صورت خودکار ایجاد نمی‌شود. یا DEFAULT_ADMIN_EMAIL را به همراه متغیرهای مربوط به رمز عبور، نام و نام کاربری تنظیم کرده و stack را اجرا کنید، یا docker compose run --rm planka npm run db:create-admin-user را اجرا کرده و به پرسش‌ها پاسخ دهید. دستور تعاملی در سرورهای اشتراکی امن‌تر است، زیرا رمز عبور وارد محیط container نمی‌شود که docker inspect بتواند آن را بخواند. فعال نگه داشتن DEFAULT_ADMIN_EMAIL پس از آن، حساب کاربری را در برابر ویرایش و حذف از طریق رابط کاربری قفل می‌کند.

Planka فایل‌های پیوست و آواتارها را کجا ذخیره می‌کند؟

در Planka 2، تمام فایل‌های بارگذاری‌شده شامل پیوست‌ها، آواتارهای کاربران و پس‌زمینه بردها در مسیر /app/data داخل container قرار دارند. این مسیر را روی یک volume نام‌گذاری‌شده mount کنید. اگر این مسیر mount نشود، فایل‌ها در لایه قابل‌نوشتن container باقی می‌مانند و با بازسازی container (که در هر ارتقای image رخ می‌دهد) از بین می‌روند. استفاده از bind mount نیز ممکن است، اما چون پردازش Node با UID 1000 اجرا می‌شود، باید sudo chown -R 1000:1000 را روی دایرکتوری میزبان اجرا کنید، در غیر این صورت بارگذاری فایل‌ها با خطای مجوز (permission error) مواجه می‌شود.

Planka برای میزبانی شخصی به چه مقدار RAM نیاز دارد؟

این پروژه حداقل سخت‌افزار مشخصی را اعلام نکرده است. عدد 2 هسته vCPU و 4 گیگابایت رم که در صفحات میزبانی تکرار می‌شود، پیش‌فرض ارائه‌دهنده است و نه یک اندازه‌گیری فنی؛ این مقدار برای یک برد کوچک بسیار سخاوتمندانه است. کل بار کاری شامل یک پردازش Node و یک پردازش Postgres است، بنابراین یک پلن با 1 هسته vCPU و 2 گیگابایت رم برای یک تیم دو تا پنج نفره کافی است. پس از یک هفته استفاده عادی، docker stats --no-stream را اجرا کنید و بر اساس اعداد واقعی خود تصمیم بگیرید. دیسک را بیشتر از حافظه زیر نظر داشته باشید، زیرا حجم فایل‌های پیوست است که افزایش می‌یابد.

چگونه Planka را بدون از دست دادن داده‌ها ارتقا دهم؟

بلافاصله پیش از ارتقا، از دیتابیس dump بگیرید و از volume فایل‌های بارگذاری‌شده نسخه پشتیبان تهیه کنید؛ به پشتیبان‌گیری شبانه اکتفا نکنید. از docker compose exec -T postgres pg_dump -U planka -d planka > planka-db.sql استفاده کنید و -T را نگه دارید تا pseudo-terminal خروجی هدایت‌شده را خراب نکند. یادداشت‌های انتشار (release notes) مربوط به تمام نسخه‌هایی که از آن‌ها عبور می‌کنید را بخوانید، تگ image را از latest به یک نسخه خاص تغییر دهید، سپس docker compose pull و docker compose up -d را اجرا کرده و لاگ را برای مشاهده فرآیند migration زیر نظر بگیرید. تگ Postgres را روی نسخه اصلی (major version) خود ثابت نگه دارید، زیرا سرور از باز کردن دایرکتوری داده‌ای که توسط نسخه اصلی دیگری نوشته شده است، خودداری می‌کند.