Hướng dẫn tự host Planka bằng Docker Compose trên VPS
Triển khai Planka với Postgres và Traefik trên VPS. Bài viết hướng dẫn cấu hình biến môi trường BASE_URL chính xác để tránh lỗi đăng nhập và thiết lập admin bootstrap cần thiết.
Những gì bạn nhận được khi tự host Planka
Tự host Planka mang lại cho đội ngũ của bạn một bảng Kanban với mô hình thẻ, danh sách và nhãn mà mọi người đã quen thuộc từ Trello, chạy trên một VPS do bạn kiểm soát. Không có giới hạn số lượng người dùng và không tính phí theo đầu người, vì chi phí duy nhất là máy chủ. Hướng dẫn này triển khai Planka bằng Docker Compose phía sau Traefik, sử dụng Postgres để lưu trữ dữ liệu và một named volume cho mọi file mà người dùng tải lên.
Đối tượng độc giả mà tôi hướng tới là các nhóm từ hai đến năm người đang rời bỏ gói miễn phí của Trello. Nếu bạn vẫn đang cân nhắc chọn bảng nào để sử dụng, hãy đọc bài so sánh các giải pháp thay thế Trello tự host trước. Hướng dẫn này giả định rằng bạn đã đưa ra quyết định và chỉ tập trung vào việc triển khai.
Bạn cần một VPS chạy Docker Engine với plugin Compose và một bản ghi DNS A trỏ tới đó. Bạn cũng cần một instance Traefik đã thực hiện TLS termination (bảo mật lớp truyền tải) trên máy chủ đó. Nếu chưa có Traefik, hãy thiết lập một reverse proxy Traefik phía trước các ứng dụng Compose trước, và đọc các kiến thức cơ bản về Docker Compose cho VPS nếu file dưới đây trông có vẻ lạ lẫm với bạn.
Planka cần VPS cấu hình bao nhiêu?
Dự án không công bố mức phần cứng tối thiểu, vì vậy hãy coi mọi con số bạn đọc được là điểm khởi đầu thay vì một phép đo chuẩn. Con số 2 vCPU và 4 GB mà các trang hướng dẫn hosting thường nhắc lại là mức mặc định an toàn của nhà cung cấp, không phải yêu cầu thực tế từ dự án. Đây là mức dư dả cho một bảng công việc có 5 người dùng.
Thực tế, các tiến trình chạy rất nhẹ: một tiến trình Node.js phục vụ API và frontend đã build, cùng một tiến trình Postgres lưu trữ dữ liệu. Một tiến trình proxy nhỏ thứ ba chạy bên trong container Planka để lọc các request đi. Một gói 1 vCPU và 2 GB là đủ cho bảng công việc từ 2 đến 5 người, và phần lớn bộ nhớ dư thừa sẽ được Postgres dùng làm cache. Một bảng công việc là ứng dụng tiêu tốn ít tài nguyên, vì vậy nếu VPS đó còn dùng để lưu trữ tài liệu cho nhóm, hãy tính toán dung lượng cho ứng dụng đó trước: chạy AFFiNE như một không gian làm việc kiểu Notion cần riêng vài GB trước khi Planka yêu cầu bất cứ tài nguyên nào.
Hãy tính toán dung lượng đĩa trước khi tính bộ nhớ, vì các file đính kèm là phần sẽ tăng dần theo thời gian. Hãy tự đo đạc instance của bạn thay vì tin hoàn toàn vào đoạn này:
docker stats --no-stream
docker system df -vLệnh đầu tiên in ra bộ nhớ và CPU thực tế trên mỗi container. Lệnh thứ hai hiển thị dung lượng mà mỗi volume đang chiếm giữ. Hãy thực hiện cả hai phép đo sau một tuần làm việc bình thường, đừng đo vào ngày cài đặt, vì một bảng công việc ở trạng thái nhàn rỗi sẽ không cho bạn biết gì về nhu cầu thực tế của nhóm.
Viết file Compose
Tạo thư mục và thiết lập quyền sở hữu để bạn không bao giờ phải chỉnh sửa các file này thông qua sudo.
sudo mkdir -p /opt/planka
sudo chown "$USER":"$USER" /opt/planka
cd /opt/plankaTạo các secret vào một file .env nằm cạnh file Compose. Compose tự động đọc file đó và thay thế các giá trị.
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 .envopenssl rand -hex là lựa chọn có chủ đích. Một chuỗi hex chỉ chứa các chữ số và chữ cái từ a đến f, nên nó không thể làm hỏng chuỗi kết nối DATABASE_URL mà nó được dán vào. Một mật khẩu base64 có chứa dấu gạch chéo hoặc dấu a-còng sẽ gây ra lỗi kết nối, trông giống như lỗi sai hostname, và bạn sẽ mất cả giờ đồng hồ để tìm nguyên nhân. Mô hình rộng hơn được đề cập trong giữ các secret bên ngoài file Compose.
Bây giờ là docker-compose.yml. Thay thế kanban.example.com bằng hostname của bạn tại cả hai vị trí xuất hiện.
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:Có bốn quyết định trong file đó cần được giải thích, vì đây là những thứ mọi người thường thay đổi rồi sau đó phải hối hận.
- Không có khối
ports:nào trên service Planka. Traefik kết nối tới container thông qua mạngproxy, vì vậy cổng 1337 không bao giờ được public ra host. Việc public cổng này sẽ tạo ra một lối đi vòng qua proxy và chứng chỉ của bạn. loadbalancer.server.port=1337chỉ định cổng bên trong container. Planka lắng nghe trên cổng 1337, và ví dụ upstream chỉ kết nối được trên cổng 3000 vì nó map cổng đó ra host. Ở đây không có mapping ra host, nên phải khai báo cổng container cho Traefik biết.condition: service_healthyđi kèm với healthcheck của Postgres. Nếu thiếu nó, Planka sẽ khởi động trước khi database sẵn sàng nhận kết nối, dẫn đến lỗi truy vấn đầu tiên và thoát, trông giống như một vòng lặp crash. Cơ chế này nằm trong healthcheck và thứ tự khởi động trong Compose.- Service database được đặt tên là
postgresmột cách có chủ đích. Planka 2 định tuyến các request gửi đi thông qua một bộ lọc nội bộ với danh sách chặn mặc định làlocalhost,postgres. Nếu đổi tên service, bạn sẽ vô tình loại bỏ database của mình khỏi danh sách đó.
Kiểm tra xem Compose có nhận được các secret của bạn trước khi bắt đầu bất cứ thứ gì không:
docker compose config | grep -E 'image:|BASE_URL|POSTGRES_USER'Lệnh này in ra file với các giá trị .env đã được thay thế. Một giá trị trống nghĩa là Compose không đọc được file .env, thường là do bạn đang chạy lệnh từ một thư mục khác.
Các biến bootstrap quản trị viên thực hiện công việc gì
Kể từ phiên bản Planka 1.13, hệ thống không tự tạo tài khoản quản trị viên cho bạn, vì vậy một database mới sẽ không có ai có quyền đăng nhập. Nhóm biến DEFAULT_ADMIN_* là một trong hai cách để giải quyết vấn đề này.
Khi khởi động, Planka tìm kiếm một người dùng khớp với DEFAULT_ADMIN_EMAIL. Nếu không tìm thấy, nó sẽ tạo một tài khoản mới sử dụng mật khẩu, tên hiển thị và tên người dùng được thiết lập kèm theo. Việc này chỉ xảy ra ở lần khởi động đầu tiên với một database trống, vì vậy các biến này dùng để bootstrap tài khoản thay vì quản lý nó.
DEFAULT_ADMIN_EMAIL thực hiện một công việc thứ hai thường gây nhầm lẫn cho người dùng. Khi biến này còn được thiết lập, tài khoản được chỉ định không thể bị chỉnh sửa hoặc xóa từ giao diện bởi bất kỳ ai. Đây là cơ chế bảo vệ chống khóa tài khoản, và cũng là lý do tại sao bạn không thể đổi tên hoặc thay đổi địa chỉ email của tài khoản đó trong UI. Hãy xóa biến này và khởi động lại, tài khoản đó sẽ trở thành một quản trị viên bình thường mà bạn có thể chỉnh sửa như mọi tài khoản khác.
Dòng mật khẩu là nơi cần cẩn thận. Bất kỳ nội dung nào nằm dưới environment: đều có thể đọc được bởi bất kỳ ai có quyền chạy docker inspect trên container, vì vậy DEFAULT_ADMIN_PASSWORD không nên tồn tại ở đó vĩnh viễn. Hãy đăng nhập, đổi mật khẩu trong giao diện, xóa dòng đó đi, sau đó chạy lại docker compose up -d.
Cách làm sạch sẽ hơn là bỏ qua hoàn toàn các biến này. Hãy comment toàn bộ nhóm DEFAULT_ADMIN_*, sau đó tạo tài khoản theo cách tương tác:
docker compose run --rm planka npm run db:create-admin-userHệ thống sẽ yêu cầu nhập email, mật khẩu, tên hiển thị và tên người dùng tùy chọn, sau đó ghi trực tiếp người dùng vào database. Mật khẩu không bao giờ xuất hiện trong file Compose hoặc môi trường của container. Hãy sử dụng cách này nếu có nhiều hơn một người có quyền truy cập shell vào VPS. Lệnh này khởi động Postgres trước nhờ vào depends_on, vì vậy nó hoạt động ngay cả trên một stack chưa từng được chạy.
Cả hai cách đều yêu cầu bạn tự quản lý mật khẩu Planka. Nếu đây là bộ thông tin đăng nhập thứ tư mà nhóm của bạn phải ghi nhớ, Planka có thể ủy quyền đăng nhập cho một nhà cung cấp OIDC như Authentik chạy như một máy chủ đăng nhập một lần (SSO) của riêng bạn, với tài khoản quản trị viên bootstrap được giữ lại làm tài khoản dự phòng (break-glass) cho những ngày nhà cung cấp dịch vụ gặp sự cố.
Tại sao BASE_URL làm hỏng đăng nhập khi không khớp với hostname
BASE_URL là địa chỉ chính xác mà người dùng nhập vào trình duyệt, bao gồm scheme và không có dấu gạch chéo ở cuối. Đối với stack này, đó là https://kanban.example.com. Planka tự tạo các liên kết và kết nối WebSocket từ giá trị đó, nghĩa là một BASE_URL sai sẽ không hiển thị lỗi rõ ràng. Bạn sẽ thấy trang web tải xong nhưng không bao giờ hiển thị nội dung.
Trường hợp phổ biến: bạn sao chép ví dụ từ upstream, để nguyên BASE_URL=http://localhost:3000 và truy cập trang web qua HTTPS trên tên miền thực của bạn. Form đăng nhập gửi đi và thông tin xác thực được chấp nhận. Tuy nhiên, bảng công việc không bao giờ xuất hiện. Mở console của trình duyệt, bạn sẽ thấy các yêu cầu đến /socket.io/ bị lỗi, vì client được chỉ định mở kết nối trực tiếp tới localhost:3000, và trên máy tính của bạn, địa chỉ đó không tồn tại.
TRUST_PROXY=true là nửa còn lại của cùng một vấn đề. Planka nằm sau Traefik, nên mọi yêu cầu đều đến từ địa chỉ của proxy qua HTTP thuần bên trong Docker network. Nếu không có TRUST_PROXY, ứng dụng sẽ bỏ qua các header X-Forwarded-Proto và X-Forwarded-For mà Traefik thiết lập, dẫn đến việc nó tin rằng kết nối không an toàn và coi mọi client là một địa chỉ IP dùng chung. Khi được thiết lập, ứng dụng sẽ đọc các header đó và đồng bộ với trình duyệt về scheme.
Traefik proxy WebSocket mà không cần cấu hình thêm, đó là lý do nên ưu tiên nó ở đây. Trên nginx, socket.io cần một block location riêng mang theo proxy_set_header Upgrade $http_upgrade và proxy_set_header Connection "upgrade", nếu không bạn sẽ gặp lỗi vòng quay tải trang bị kẹt do nguyên nhân khác.
Việc chuyển bảng công việc sang một hostname mới sau này đồng nghĩa với việc phải thay đổi hai thứ cùng lúc: giá trị BASE_URL và rule Host() của Traefik. Thay đổi một cái mà quên cái còn lại, bạn sẽ lại gặp lỗi vòng quay tải trang. Việc phục vụ Planka từ một subpath như https://example.com/planka hoạt động từ phiên bản 2.1.0 trở đi, được phát hành vào tháng 3 năm 2026. Với các tag cũ hơn, hãy cấp cho nó một subdomain riêng.
Nơi Planka lưu trữ tệp đính kèm và ảnh đại diện
Planka 2 lưu trữ mọi thứ người dùng tải lên tại một đường dẫn duy nhất bên trong container: /app/data. Các tệp đính kèm, ảnh đại diện người dùng và hình nền bảng đều nằm tại đây. Phiên bản 1 sử dụng ba thư mục riêng biệt, vì vậy tệp Compose sao chép từ các hướng dẫn cũ sẽ mount vào các đường dẫn không còn tồn tại, khiến thư mục dữ liệu thực tế không được mount.
Việc mount thư mục này là yếu tố quyết định giữa một bảng dữ liệu còn nguyên vẹn sau khi nâng cấp và một buổi chiều tồi tệ. Nếu /app/data không nằm trên một volume, các tệp tải lên sẽ nằm trong lớp writable layer của container. Lớp này bị xóa khi container được tạo lại, và container sẽ bị tạo lại mỗi khi bạn thay đổi image tag. Bảng dữ liệu vẫn hiển thị bình thường, các thẻ vẫn còn đó, nhưng mọi liên kết tệp đính kèm đều bị hỏng vì các dòng trong database vẫn trỏ đến những tệp không còn tồn tại.
Named volume trong tệp Compose ở trên ngăn chặn tình trạng này. Bind mount cũng hoạt động hiệu quả và giúp việc sao lưu tệp bằng các công cụ thông thường dễ dàng hơn, nhưng nó cần thêm một bước. Tiến trình Node bên trong container chạy với UID 1000, vì vậy nếu thư mục trên host thuộc sở hữu của root, bạn sẽ gặp lỗi quyền truy cập ngay lần tải lên đầu tiên:
sudo chown -R 1000:1000 /opt/planka/dataSự đánh đổi giữa hai phương pháp này được phân tích chi tiết tại bind mounts so với named volumes.
Nếu dung lượng tệp đính kèm vượt quá giới hạn ổ cứng trên gói dịch vụ của bạn, Planka có thể ghi chúng vào storage tương thích với S3 thông qua S3_ENDPOINT, S3_BUCKET và các biến key tương ứng. Bạn có thể trỏ nó đến một bucket trên cloud hoặc một MinIO object store tự host trên một máy chủ khác. Hãy quyết định việc này trước khi nhóm của bạn bắt đầu sử dụng bảng, vì cài đặt này chỉ áp dụng cho các tệp tải lên mới.
Khởi động stack và kiểm tra kết quả
docker compose pull
docker compose up -d
docker compose psdocker compose ps sẽ hiển thị postgres là healthy và planka là running. Nếu Planka khởi động lại liên tục, hãy kiểm tra kết nối cơ sở dữ liệu trước, thay vì kiểm tra ứng dụng.
docker compose logs -f plankaMột lần khởi động đầu tiên thành công sẽ chạy các migration cơ sở dữ liệu và sau đó báo cáo server đang lắng nghe trên cổng 1337. Hãy xác nhận schema đã được tạo bằng cách truy vấn trực tiếp Postgres thay vì chỉ tin vào log:
docker compose exec postgres psql -U planka -d planka -c '\dt'Danh sách bảng bao gồm board và card nghĩa là các migration đã chạy thành công. Thông báo "Did not find any relations" nghĩa là Planka chưa kết nối được, vì vậy hãy so sánh DATABASE_URL với các giá trị POSTGRES_USER và POSTGRES_PASSWORD trong file .env của bạn.
Sau đó, hãy kiểm tra route từ máy tính của bạn, không phải từ VPS:
curl -I https://kanban.example.comHTTP/2 200 nghĩa là Traefik đã có chứng chỉ và đang kết nối được với container. Lỗi 404 do Traefik trả về nghĩa là các nhãn router không khớp, thường là do container chưa được gắn vào network proxy. Bây giờ hãy mở trang web và đăng nhập bằng tài khoản admin.
Thực hiện pg_dump trước mỗi lần nâng cấp phiên bản
Dữ liệu của bạn được lưu trữ tại hai nơi riêng biệt, vì vậy bản sao lưu phải bao gồm cả hai: cơ sở dữ liệu Postgres và volume planka-data. Hãy dump cơ sở dữ liệu khi stack đang chạy.
docker compose exec -T postgres pg_dump -U planka -d planka > "planka-db-$(date +%F).sql"Tham số -T là bắt buộc. Nếu không có nó, Compose sẽ cấp phát một pseudo-terminal, và lớp terminal sẽ ghi đè các ký tự xuống dòng trong luồng dữ liệu, dẫn đến file dump bị lỗi khi restore. Lỗi này thường xuất hiện sau vài tuần, thời điểm tồi tệ nhất để gặp sự cố.
Tiếp theo là phần uploads. Trước tiên hãy tìm tên volume thực tế, vì Compose sẽ thêm tiền tố là tên thư mục dự án vào tên volume.
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 .Dự án cũng cung cấp sẵn docker-backup.sh và docker-restore.sh trong repository, và tài liệu chính thức khuyến nghị chạy chúng bằng cron job hàng đêm. Cách nào cũng được. Điều không ổn là bạn chưa bao giờ thử restore bản sao lưu, vì vậy hãy restore thử một lần lên một VPS trống và xác nhận rằng bạn có thể đăng nhập và mở file đính kèm. Cặp lưu trữ này xuất hiện trong mọi ứng dụng Compose có hỗ trợ upload, vì vậy nếu sau này bạn cài Chatwoot trên cùng máy chủ với bộ phận hỗ trợ, quy trình bạn xây dựng tại đây vẫn có thể áp dụng lại, chỉ cần thay đổi tên volume.
Hãy thực hiện dump ngay trước mỗi lần thay đổi phiên bản. Bản sao lưu từ đêm qua không giống với bản sao lưu trước khi thực hiện migration mà bạn sắp chạy.
Ghim các tag và đọc ghi chú phát hành
Cả hai image tag trong file đó đều được ghim một cách có chủ đích.
ghcr.io/plankanban/planka:2.1.1 là một bản phát hành cụ thể, cập nhật tính đến tháng 8 năm 2026. latest sẽ thay đổi bất cứ khi nào phía upstream xuất bản, vì vậy một lệnh docker compose pull định kỳ có thể kéo theo một thay đổi schema vào thời điểm bạn không mong muốn. Hãy đọc ghi chú phát hành trước khi bạn thay đổi con số đó, vì đây là nơi mô tả các thay đổi gây lỗi (breaking changes) và các bản vá bảo mật. Phiên bản 2.0.3 được phát hành như một bản cập nhật bảo mật, đây chính xác là loại thông tin bạn cần đọc thay vì vô tình tiếp nhận. Việc ghim tag ở đây rất dễ dàng vì phía upstream có xuất bản các image, và ở những dự án không cung cấp image, bạn cần áp dụng kỷ luật tương tự với một bước bổ sung, như với openGym được build trên máy từ một git tag đã checkout.
postgres:16-alpine được ghim vào một phiên bản chính (major version) vì lý do nghiêm ngặt hơn. Postgres ghi thư mục dữ liệu của nó theo định dạng gắn liền với phiên bản chính, và server sẽ từ chối mở thư mục được ghi bởi một phiên bản khác. Nếu bạn ghi postgres:latest, để tag tự động cập nhật lên 17, container sẽ không khởi động được:
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.Không có dữ liệu nào bị mất, và việc khởi động lại cũng không giải quyết được vấn đề. Chuyển sang một phiên bản Postgres chính mới đồng nghĩa với việc dump dữ liệu từ phiên bản cũ và restore vào một thư mục dữ liệu mới trên phiên bản mới. Đây là một công việc cần lên kế hoạch khi stack đang dừng, không phải là tác dụng phụ của một lệnh image pull.
Nếu bạn đang chuyển đổi một bản cài đặt Planka 1.x hiện có thay vì bắt đầu mới, quá trình nâng cấp đó có quy trình riêng được ghi lại trong tài liệu dự án, và không có cách nào quay lại phiên bản 1 nếu không có bản backup được thực hiện từ trước.
Các chế độ lỗi và thông báo bạn sẽ gặp
Planka khởi động lại liên tục và log báo lỗi cơ sở dữ liệu. Thông tin đăng nhập trong DATABASE_URL không khớp với môi trường Postgres. Lưu ý rằng POSTGRES_PASSWORD chỉ được áp dụng khi thư mục dữ liệu được khởi tạo lần đầu, vì vậy việc sửa biến này sau một lần khởi động lỗi sẽ không có tác dụng. Bạn phải xóa volume db-data và bắt đầu lại.
Đăng nhập thành công nhưng bảng không bao giờ tải. BASE_URL không khớp với địa chỉ trên thanh trình duyệt, hoặc TRUST_PROXY bị thiếu. Console của trình duyệt hiển thị các request thất bại đến /socket.io/.
Upload thất bại trong khi mọi thứ khác vẫn hoạt động. Một bind mount đang thuộc sở hữu của root. Hãy chạy sudo chown -R 1000:1000 trên thư mục host và khởi động lại container.
Tệp đính kèm biến mất sau khi nâng cấp. /app/data không nằm trên một volume, nên các tệp tin nằm trong lớp container đã bị thay thế khi nâng cấp. Hãy khôi phục tệp từ bản backup, sau đó thêm volume trước khi bạn thay đổi image tag lần nữa.
Traefik trả về lỗi 404. Container không nằm trên network proxy, hoặc rule Host() không khớp với bản ghi DNS của bạn. docker compose config hiển thị các label sau khi thay thế, đây là nơi các lỗi đánh máy sẽ lộ diện.
Thông báo hoặc webhook không bao giờ đến nơi. Planka 2 gửi các request HTTP đi qua một bộ lọc nội bộ, và danh sách chặn mặc định bao gồm localhost và postgres. Một webhook hướng đến container khác trên cùng host có thể bị chặn theo thiết kế. Hãy điều chỉnh OUTGOING_ALLOWED_HOSTS thay vì xóa bộ lọc.
Khi đã chạy ổn định, tải vận hành rất thấp. Hãy theo dõi các release note và dump cơ sở dữ liệu trước mỗi lần nâng cấp. Một lần reboot sẽ tự động khôi phục stack nhờ restart: unless-stopped, miễn là dịch vụ Docker đã được enable khi boot, và Các Compose stack tự khởi động sau khi reboot sẽ giải quyết các trường hợp không tự chạy.
FAQ
Tại sao Planka cứ tải mãi sau khi tôi đăng nhập?
Thông tin đăng nhập đã được chấp nhận nhưng kết nối trực tiếp thì không. Planka xây dựng URL WebSocket từ BASE_URL, vì vậy nếu biến này vẫn là http://localhost:3000 trong khi bạn truy cập trang web tại https://kanban.example.com, trình duyệt sẽ cố mở socket đến một địa chỉ không tồn tại trên máy của bạn. Bảng điều khiển dành cho nhà phát triển (developer console) sẽ hiển thị các yêu cầu thất bại đến /socket.io/. Hãy đặt BASE_URL thành địa chỉ công khai chính xác không có dấu gạch chéo ở cuối, thêm TRUST_PROXY=true để ứng dụng tuân thủ header X-Forwarded-Proto từ reverse proxy của bạn, sau đó chạy docker compose up -d.
Làm thế nào để tạo người dùng quản trị đầu tiên cho Planka?
Kể từ phiên bản 1.13, không có quản trị viên nào được tạo tự động. Bạn có thể đặt DEFAULT_ADMIN_EMAIL cùng với các biến mật khẩu, tên và tên người dùng tương ứng rồi khởi động stack, hoặc chạy docker compose run --rm planka npm run db:create-admin-user và trả lời các prompt. Lệnh tương tác an toàn hơn trên máy chủ dùng chung vì mật khẩu không bao giờ đi vào môi trường container nơi docker inspect có thể đọc được nó. Việc giữ DEFAULT_ADMIN_EMAIL sau đó sẽ khóa tài khoản đó, không cho phép chỉnh sửa hoặc xóa từ giao diện.
Planka lưu trữ tệp đính kèm và ảnh đại diện ở đâu?
Tất cả các tệp đã tải lên nằm trong /app/data bên trong container ở Planka 2, bao gồm tệp đính kèm, ảnh đại diện người dùng và hình nền bảng. Hãy mount đường dẫn đó vào một volume có tên. Nếu không được mount, các tệp sẽ nằm trong lớp ghi được (writable layer) của container và bị xóa vào lần tiếp theo container được tạo lại, điều này xảy ra mỗi khi nâng cấp image. Bind mount cũng hoạt động, nhưng tiến trình Node chạy với UID 1000, vì vậy hãy chạy sudo chown -R 1000:1000 trên thư mục host nếu không việc tải lên sẽ thất bại do lỗi quyền truy cập.
Planka tự host cần bao nhiêu RAM?
Dự án không công bố yêu cầu phần cứng tối thiểu. Con số 2 vCPU và 4 GB thường thấy trên các trang hosting là mặc định của nhà cung cấp chứ không phải phép đo, và nó khá dư thừa cho một bảng nhỏ. Một tiến trình Node và một tiến trình Postgres là toàn bộ khối lượng công việc, vì vậy gói 1 vCPU và 2 GB là đủ cho một nhóm từ hai đến năm người. Hãy chạy docker stats --no-stream sau một tuần sử dụng bình thường và tự điều chỉnh kích thước dựa trên số liệu của riêng bạn. Hãy theo dõi dung lượng đĩa sát sao hơn bộ nhớ, vì tệp đính kèm là thứ sẽ tăng lên theo thời gian.
Làm thế nào để nâng cấp Planka mà không mất dữ liệu?
Hãy dump cơ sở dữ liệu và lưu trữ volume chứa tệp tải lên ngay trước khi nâng cấp, đừng dựa vào lịch trình sao lưu của đêm hôm trước. Sử dụng docker compose exec -T postgres pg_dump -U planka -d planka > planka-db.sql, giữ lại -T để pseudo-terminal không làm hỏng đầu ra được chuyển hướng. Đọc ghi chú phát hành cho mọi phiên bản bạn bỏ qua, thay đổi tag của image thành một bản phát hành cụ thể thay vì latest, sau đó chạy docker compose pull và docker compose up -d rồi theo dõi log để xem quá trình migration. Giữ tag của Postgres cố định ở phiên bản major của nó, vì máy chủ sẽ từ chối mở thư mục dữ liệu được ghi bởi một phiên bản major khác.