Plane Docker Compose 部署实战
Plane 社区版 v1.4.2 官方 Compose 通道部署实测:setup.sh 获取编排、plane.env 定制(端口/密钥)、13 服务逐一起停与状态核验、实例初始化、工作区/项目/Issue/周期/模块创建、MinIO 附件预签名直传验证、官方 k8s(Helm)形态溯源与 7 条排障实录(2026-10-27)。
Plane Docker Compose 部署实战
目标:在一台 Linux 服务器上用官方 Docker Compose 通道完整部署 Plane(makeplane/plane,官网 plane.so)社区版 v1.4.2 多服务栈,完成实例初始化、工作区/项目/工作项/周期/模块创建与对象存储附件上传验证,并沉淀本次部署中的真实排障记录。已在 Ubuntu 22.04 服务器(Docker 29.9.2 + Compose v5.6.1)实测通过(2026-10-27)。
一、环境信息
- 操作系统:Ubuntu 22.04 LTS(x86_64,内核 6.x)
- 资源配置:6 vCPU / 19 GB 内存 / 数据盘可用 291 GB(多服务栈建议至少 4 vCPU / 8 GB 内存起步)
- 软件版本:Docker 29.9.2、Docker Compose v5.6.1(v2 语法插件)、Plane 社区版 v1.4.2(官方 GitHub Releases 最新稳定版,另有 v1.4.3-rc 预发布未采用;版本核实日期 2026-10-27)
- 依赖组件(随官方编排一同部署):PostgreSQL 15.7(alpine)、Valkey 7.2.11(alpine,Redis 兼容)、RabbitMQ 3.13.6(management-alpine)、MinIO(
docker.io/pgsty/minio:RELEASE.2026-08-04T00-00-00Z) - 部署目录:
/opt/data/quzuo/tmp/plane/cli/plane-app(含docker-compose.yaml与plane.env两个文件) - Compose 项目名:
plane-1.4.2-stack(在编排文件顶部显式name:声明;Compose 规范化后为plane-142-stack) - 自定义网络:显式
--subnet 10.89.66.0/24(部署前用docker network ls核对过 10.89.x.0/24 槽位未被占用) - 服务端口:HTTP 8145 / HTTPS 8146(官方默认 80/443 与本机已有服务冲突,改用 8128–8152 区间空闲端口)
- 演示账号:实例管理员
admin@plane.local/plane@admin2026(密码策略原因未采用 q1w2e3r4,详见 4.6 节与第七章)
二、镜像与编排选型说明
Plane 社区版官方不提供单进程安装包,自托管主线就是官方 setup.sh 安装器 + 一份 docker-compose.yml 编排文件。setup.sh install 会从 GitHub Release 下载两份文件(下载地址在脚本内写死,可从脚本源码核对):
https://github.com/makeplane/plane/releases/download/<版本号>/docker-compose.yml→ 落为plane-app/docker-compose.yamlhttps://github.com/makeplane/plane/releases/download/<版本号>/variables.env→ 落为plane-app/plane.env
v1.4.2 编排文件定义了 13 个服务,全部走官方 makeplane/* 系列镜像(标签统一受 APP_RELEASE 变量控制):
| 服务名 | 镜像 | 角色 | 是否必须 |
|---|---|---|---|
| web | makeplane/plane-frontend | 工作台前端(Next.js) | 是 |
| space | makeplane/plane-space | 对外发布页/站点前端 | 是 |
| admin | makeplane/plane-admin | 实例管理端(God Mode)前端 | 是 |
| live | makeplane/plane-live | 实时协作 WebSocket 服务 | 是 |
| api | makeplane/plane-backend | Django API 主服务(gunicorn) | 是 |
| worker | makeplane/plane-backend | Celery 异步任务 | 是 |
| beat-worker | makeplane/plane-backend | Celery 周期调度 | 是 |
| migrator | makeplane/plane-backend | 启动期数据库迁移(一次性,跑完即退出) | 是 |
| plane-db | postgres:15.7-alpine | 业务数据库 | 是 |
| plane-redis | valkey/valkey:7.2.11-alpine | 缓存/队列中间件 | 是 |
| plane-mq | rabbitmq:3.13.6-management-alpine | Celery 消息队列 | 是 |
| plane-minio | docker.io/pgsty/minio:RELEASE.2026-08-04T00-00-00Z | S3 兼容对象存储(附件) | 是(也可接外部 S3) |
| proxy | makeplane/plane-proxy | 统一入口反向代理(含 ACME 证书能力) | 是(也可换自有网关) |
💡 技术拓展:v1.4.2 编排中的对象存储镜像是
pgsty/minio而非早期版本的minio/minio。两者同为 S3 兼容对象存储服务端:MinIO 官方镜像(AGPL)在 2025 年后仅发布源码版、二进制发行收紧,社区出现了若干再分发版;pgsty/minio是 Pigsty 项目维护的 MinIO 再打包镜像,保留官方控制台能力。Plane 官方在 v1.4.2 编排里直接替换为该镜像并保留RELEASE.2026-08-04T00-00-00Z版本标签,属于发行侧的镜像来源调整,API 协议不变(2026-10-27 对 v1.4.2 编排文件实查)。
⚠️ 注意:
plane-db/plane-redis/plane-mq/plane-minio四个基础服务在编排文件里都有注释行「Comment this if you already have…」,官方明确支持替换为外部 PostgreSQL/Redis/RabbitMQ/S3——替换时同时要把plane.env里的对应连接变量改掉(详见 C1 配置篇)。
三、前置条件
- 服务器已安装 Docker Engine 与 Compose v2 插件:
docker --version && docker compose version
# Docker version 29.9.2, build a82e528
# Docker Compose version v5.6.1
- 规划端口未被占用(官方默认 80/443,本文改用 8145/8146):
ss -ltn | grep -E ':(8145|8146)\b' || echo "端口空闲"
# 端口空闲
-
磁盘空间:本次部署拉取完全套服务镜像后,Plane 栈占用约 4 GB;数据卷另计。
-
网络说明:Docker Hub 拉取
makeplane/*与基础镜像在国内网络环境下较慢时,本环境已配置 Docker 拉取代理192.168.1.62:7897,本次全部 13 个服务镜像均经代理直拉成功,无需手动-x降级(⚠️ 实测可用渠道:代理直拉;未测试离线导入路径)。 -
若使用自定义网段,先核对 10.89.x.0/24 空闲槽位:
docker network ls | grep -c 10.89 || true
# 确认已在用的 10.89.x 网段,从空闲槽位中选取(本文取 10.89.66.0/24)
四、部署步骤
4.1 获取官方安装器与编排文件
官方入口是一行 curl(可先下载审阅再执行,本文采用后者):
curl -fsSL https://raw.githubusercontent.com/makeplane/plane/master/deployments/cli/setup.sh -o setup.sh
setup.sh 是一个交互式菜单安装器,动作集合在脚本内固定为 8 项(Install/Start/Stop/Restart/Upgrade/View Logs/Backup Data/Exit)。本文部署路径为:先用 install 语义获取官方文件,再按环境改造 plane.env,最后等价于 start 地启动。实际操作如下:
cd /opt/data/quzuo/tmp/plane
bash setup/setup.sh install 2>&1 | tail -6
# Begin Installing Plane
# Checking for the latest release...
# v1.4.2
# Plane supports x86_64
# Begin Installing Plane
# ...
# Most recent version of Plane is now available for you to use
执行后目录中出现 plane-app/(含 docker-compose.yaml、plane.env)。
4.2 定制部署参数(plane.env)
官方生成的 plane.env 需要按环境改三处:入口端口、域名与密钥。密钥生成命令官方注释里给出两种:
openssl rand -hex 32
# 027fdf1b74f8a24ffca26bc1ee380568f25a46910b8707d1c8a63ebab356344b
本文改后的关键字段(plane.env 节选,实测生效值):
APP_DOMAIN=localhost
APP_RELEASE=v1.4.2
LISTEN_HTTP_PORT=8145
LISTEN_HTTPS_PORT=8146
WEB_URL=http://localhost:8145
DEBUG=0
CORS_ALLOWED_ORIGINS=http://localhost:8145
API_BASE_URL=http://api:8000
# Secret Key — change this before deploying to production.
SECRET_KEY=027fdf1b74f8a24ffca26bc1ee380568f25a46910b8707d1c8a63ebab356344b
USE_MINIO=1
AWS_ACCESS_KEY_ID=access-key
AWS_SECRET_ACCESS_KEY=secret-key
AWS_S3_ENDPOINT_URL=http://plane-minio:9000
AWS_S3_BUCKET_NAME=uploads
FILE_SIZE_LIMIT=5242880
# Live server environment variables — change this before deploying to production.
LIVE_SERVER_SECRET_KEY=20390b33bbb5d436e25f81ef99bc832a5b1315b7cfcaf7a6c64885ad0d115579
⚠️ 注意:
SECRET_KEY与LIVE_SERVER_SECRET_KEY官方模板默认值均为change-this-key-on-deployment,必须替换;SECRET_KEY一旦更换,全部既有登录会话立即失效(实测见第七章 Q3)。演示值仅用于本文实验环境,生产请用openssl rand -hex 32现生成。
4.3 编排文件的本地化改造
为满足项目名与网段规划,在官方 docker-compose.yaml 顶部追加两段(其余 257 行保持官方原样):
name: plane-1.4.2-stack
networks:
default:
driver: bridge
ipam:
config:
- subnet: 10.89.66.0/24
Compose 对项目名做规范化(去点号),实际生效的项目名为 plane-142-stack,可用 docker compose ps 核对。
4.4 启动服务栈
等价于官方 setup.sh start 的命令与实测回显(首次启动先拉镜像,约数分钟):
cd /opt/data/quzuo/tmp/plane/cli/plane-app
docker compose -f docker-compose.yaml --env-file plane.env up -d 2>&1 | tail -15
# Container plane-142-stack-worker-1 Running
# Container plane-142-stack-api-1 Running
# Container plane-142-stack-web-1 Running
# Container plane-142-stack-plane-db-1 Running
# Container plane-142-stack-beat-worker-1 Running
# Container plane-142-stack-migrator-1 Starting
# Container plane-142-stack-admin-1 Starting
# Container plane-142-stack-live-1 Starting
# Container plane-142-stack-space-1 Starting
# Container plane-142-stack-migrator-1 Started
# Container plane-142-stack-admin-1 Started
# Container plane-142-stack-live-1 Started
# Container plane-142-stack-space-1 Started
# Container plane-142-stack-proxy-1 Starting
# Container plane-142-stack-proxy-1 Started
💡 说明:
migrator只在启动期执行数据库迁移,完成后以退出码 0 结束属正常状态;setup.sh start内部也会等待迁移结束并检查 api 健康后才打印Plane Server started successfully ✅。
4.5 部署状态核验
docker compose -f docker-compose.yaml --env-file plane.env ps --format 'table {{.Name}}\t{{.Service}}\t{{.Status}}'
# NAME SERVICE STATUS
# plane-142-stack-admin-1 admin Up 30 seconds (healthy)
# plane-142-stack-api-1 api Up 12 minutes
# plane-142-stack-beat-worker-1 beat-worker Up 12 minutes
# plane-142-stack-live-1 live Up 30 seconds
# plane-142-stack-plane-db-1 plane-db Up 12 minutes
# plane-142-stack-plane-minio-1 plane-minio Up 12 minutes
# plane-142-stack-plane-mq-1 plane-mq Up 12 minutes
# plane-142-stack-plane-redis-1 plane-redis Up 12 minutes
# plane-142-stack-proxy-1 proxy Up 30 seconds
# plane-142-stack-space-1 space Up 30 seconds (healthy)
# plane-142-stack-web-1 web Up 12 minutes (healthy)
# plane-142-stack-worker-1 worker Up 12 minutes
12 个常驻服务全部 Up(migrator 已正常退出),入口 HTTP 探活:
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost:8145/
# HTTP 200
浏览器访问 http://<服务器IP>:8145 即进入 Plane 初始化引导。
4.6 实例初始化(首个实例管理员)
首次访问会先要求创建「实例管理员」。用接口方式复现该引导页提交(God Mode 注册端点使用表单编码 + CSRF 双重提交防护):
cd /opt/data/quzuo/tmp/plane
# 1) 取 CSRF token(同时写入 cookie jar)
curl -s -c cj.txt http://localhost:8145/auth/get-csrf-token/ | head -c 120
# {"csrf_token":"8MxH1Q0B4mFY...(40 位)","secret_key":"..."}
CSRF=$(grep csrftoken cj.txt | awk '{print $7}')
# 2) 注册实例管理员(表单编码)
curl -s -D - -o /dev/null -b cj.txt -c cj.txt \
-X POST http://localhost:8145/api/instances/admins/sign-up/ \
-H "X-CSRFToken: $CSRF" -H 'Referer: http://localhost:8145/' \
--data-urlencode 'email=admin@plane.local' \
--data-urlencode 'password=plane@admin2026' \
--data-urlencode 'first_name=Admin' \
--data-urlencode 'is_telemetry_enabled=True' | grep -i 'HTTP/\|location'
# HTTP/1.1 302 Found
# Location: http://localhost:8145/god-mode/general/
302 跳转到 /god-mode/general/ 即实例初始化完成。此时登录应用端(/auth/sign-in/,同样表单编码):
curl -s -o /dev/null -w 'sign-in %{http_code} -> %{redirect_url}\n' \
-b cj.txt -c cj.txt -X POST http://localhost:8145/auth/sign-in/ \
-H "X-CSRFToken: $CSRF" -H 'Referer: http://localhost:8145/' \
--data-urlencode 'email=admin@plane.local' \
--data-urlencode 'password=plane@admin2026'
# sign-in 302 -> http://localhost:8145/
⚠️ 注意(实测密码策略):Plane 对所有账号密码做两道校验——Django 内置校验器(最短 8 位 + 常见密码黑名单)与
zxcvbn熵值校验(score<3 拒绝)。演示惯例密码q1w2e3r4实测两道都过不去(This password is too common.,zxcvbn 得分 1),故本文演示密码采用plane@admin2026(zxcvbn 得分 4)。
4.7 建工作区、项目、工作项、周期与模块
登录态下逐级创建业务对象(均为 JSON + CSRF 头的 REST 调用,实测回显节选;后两步直接复用 4.7 前两步返回的对象 ID,本文以变量代入:P=e1b3e0a1-7784-48de-a54a-1aa6a22de187(项目)、I=aafa600d-238c-4dcd-9eca-03ab82876dec(工作项)):
# 建工作区
curl -s -b cj.txt -X POST http://localhost:8145/api/workspaces/ \
-H "Content-Type: application/json" -H "X-CSRFToken: $CSRF" \
-H 'Referer: http://localhost:8145/' \
-d '{"name":"Demo Workspace","slug":"demo-ws"}' -w '\nHTTP %{http_code}\n' | tail -2
# HTTP 201
# (返回体含 "slug":"demo-ws"、"role":20(管理员))
# 建项目
curl -s -b cj.txt -X POST http://localhost:8145/api/workspaces/demo-ws/projects/ \
-H "Content-Type: application/json" -H "X-CSRFToken: $CSRF" \
-H 'Referer: http://localhost:8145/' \
-d '{"name":"Website Revamp","identifier":"WEB"}' -o proj.json -w 'HTTP %{http_code}\n'
# HTTP 201
python3 -c "import json;d=json.load(open('proj.json'));print('project_id:',d['id'])"
# project_id: e1b3e0a1-7784-48de-a54a-1aa6a22de187
# 建工作项(Issue)
curl -s -b cj.txt -X POST http://localhost:8145/api/workspaces/demo-ws/projects/$P/issues/ \
-H "Content-Type: application/json" -H "X-CSRFToken: $CSRF" \
-H 'Referer: http://localhost:8145/' \
-d '{"name":"Design new landing page","priority":"high","description_html":"<p>首页改版设计稿与验收标准</p>"}' -o issue.json -w 'HTTP %{http_code}\n'
# HTTP 201
python3 -c "import json;d=json.load(open('issue.json'));print('issue_id:',d['id'],'seq:',d.get('sequence_id'))"
# issue_id: aafa600d-238c-4dcd-9eca-03ab82876dec seq: 1
# 建周期(Cycle,对应迭代)
curl -s -b cj.txt -X POST http://localhost:8145/api/workspaces/demo-ws/projects/$P/cycles/ \
-H "Content-Type: application/json" -H "X-CSRFToken: $CSRF" \
-H 'Referer: http://localhost:8145/' \
-d '{"name":"Sprint 1","start_date":"2026-10-27","end_date":"2026-11-09"}' -o cycle.json -w 'HTTP %{http_code}\n'
# HTTP 201
# 建模块(Module)
curl -s -b cj.txt -X POST http://localhost:8145/api/workspaces/demo-ws/projects/$P/modules/ \
-H "Content-Type: application/json" -H "X-CSRFToken: $CSRF" \
-H 'Referer: http://localhost:8145/' \
-d '{"name":"Frontend Module"}' -o module.json -w 'HTTP %{http_code}\n'
# HTTP 201
4.8 附件上传与对象存储链路验证(内置 MinIO)
v1.4.2 的附件上传走「登记 → 预签名直传 → 回执」三步(官方前端同款流程):
# 1) 登记:声明文件名/类型/大小,换取预签名上传凭证
curl -s -b cj.txt -X POST "http://localhost:8145/api/assets/v2/workspaces/demo-ws/projects/$P/issues/$I/attachments/" \
-H "X-CSRFToken: $CSRF" -H 'Referer: http://localhost:8145/' \
-F 'name=attach-test.txt' -F 'type=text/plain' -F 'size=42' -o att2.json -w 'reg HTTP %{http_code}\n'
# reg HTTP 200
python3 -c "import json;d=json.load(open('att2.json'));print('url:',d['upload_data']['url']);print('key:',d['upload_data']['fields']['key'])"
# url: http://localhost:8145/uploads
# key: af59956c-8f00-4802-a21a-01b7f32c8cac/069d1559242141a6aec39420397c7438-attach-test.txt
# 2) 直传:把凭证里的全部字段作为表单域 + file 域一起 POST 到预签名地址
curl -s -o /dev/null -w 'upload HTTP %{http_code}\n' -X POST http://localhost:8145/uploads \
-F 'Content-Type=text/plain' -F 'key=<凭证中的 key>' -F 'x-amz-algorithm=AWS4-HMAC-SHA256' \
-F 'x-amz-credential=<凭证值>' -F 'x-amz-date=<凭证值>' -F 'policy=<凭证值>' \
-F 'x-amz-signature=<凭证值>' -F 'file=@attach-test.txt;type=text/plain'
# upload HTTP 204
# 3) 回执:标记上传完成
curl -s -o /dev/null -w 'PATCH HTTP %{http_code}\n' -b cj.txt \
-X PATCH "http://localhost:8145/api/assets/v2/workspaces/demo-ws/<asset_id>/" \
-H "Content-Type: application/json" -H "X-CSRFToken: $CSRF" \
-H 'Referer: http://localhost:8145/' -d '{"is_uploaded": true}'
# PATCH HTTP 204
对象存储侧核验:附件对象已按 <工作区ID>/<uuid>-<文件名> 落入 uploads 桶:
docker exec plane-142-stack-plane-minio-1 sh -c 'ls -R /export/uploads | head -6'
# /export/uploads:
# af59956c-8f00-4802-a21a-01b7f32c8cac
#
# /export/uploads/af59956c-8f00-4802-a21a-01b7f32c8cac:
# 295b4492cb44473c92a81a7729000730-attach-test.txt
数据库侧再核对资产状态:
docker exec -i plane-142-stack-api-1 python manage.py shell -c "from plane.db.models import FileAsset; a=FileAsset.objects.order_by('-created_at').first(); print('is_uploaded:', a.is_uploaded, '| name:', a.attributes.get('name'))" 2>/dev/null
# 105 objects imported automatically (use -v 2 for details).
# is_uploaded: True | name: attach-test.txt
对象存储链路(API → 预签名 → 代理转发 → MinIO 落盘 → 回执)全程验证通过。预签名地址是入口域名的 /uploads 路径,由 proxy 服务转发到对象存储,因此外部浏览器也能按同一流程直传。
4.9 k8s 部署形态(官方文档溯源)
本节为联网溯源内容,来源:Plane 官方自托管文档
https://developers.plane.so/self-hosting/overview与.../methods/kubernetes(页面标注 Last updated: Oct 11, 2026;本文查询日期 2026-10-27)。k8s 集群实测由 K1 批次统一执行,本节不含集群实测回显,所有命令均照抄官方文档。
官方自托管文档给出两条部署通道:
| 通道 | 版本线 | 定位 | 依据 |
|---|---|---|---|
| Docker Compose(本文主线) | 社区版 v1.4.x | 快速上手,中小团队 | 官方「Deployment methods」页:Compose 面向 small to medium teams |
| Kubernetes(Helm) | 商业版 v3.3.3(chart 当前版本线) | 生产级高可用/自动扩缩 | 官方 Kubernetes 方法页标题即「Kubernetes Commercial Edition」 |
k8s 通道要点(照抄官方文档):
- 图表仓库与安装命令:
helm repo add plane https://helm.plane.so/,随后helm upgrade --install plane-app plane/plane-enterprise --create-namespace --namespace plane --set license.licenseDomain=${DOMAIN_NAME} --set license.licenseServer=https://prime.plane.so --set planeVersion=${PLANE_VERSION} --set ingress.enabled=true --set ingress.ingressClass=nginx --set env.storageClass=longhorn --timeout 10m --wait --wait-for-jobs。 - 版本变量必须写明确版本号(官方示例
PLANE_VERSION=v3.3.3),官方明确警告不要设为stable。 - Quick setup 会在集群内自建 Postgres、Redis/Valkey、MinIO 的有状态负载(PVC 使用
longhorn存储类),并用nginxIngress 暴露入口;生产建议按官方「Configuration settings」接外部数据库与对象存储。 - 商业版从 Free 计划起步(每工作区 12 席位),付费特性需绑定 license key;已有社区实例可按官方「Upgrade to Commercial Edition」页迁移。
- 官方另提供受限 Pod 安全标准(
restricted)下的非 root 运行指引(页面kubernetes-non-root)。
⚠️ 注意:社区版(AGPL,GitHub Releases 的 v1.4.x)官方主推的安装形态就是本文的 Compose 通道;Helm 图表当前面向商业版,二者版本线不同(v1.4.x 与 v3.3.3),选型时不要混用版本号。
五、数据持久化说明
官方编排声明了 11 个命名数据卷,重建服务栈不会丢数据,down 不带 -v 也不会删卷:
| 卷名 | 挂载点 | 内容 |
|---|---|---|
| pgdata | plane-db 的 /var/lib/postgresql/data | 全部业务数据(工作区/项目/工作项…) |
| redisdata | plane-redis 的 /data | 缓存 |
| rabbitmq_data | plane-mq 的 /var/lib/rabbitmq | 队列持久化 |
| uploads | plane-minio 的 /export | 附件对象存储 |
| logs_api / logs_worker / logs_beat-worker / logs_migrator | 各后端服务的 /code/plane/logs | 服务日志文件 |
| proxy_config / proxy_data | proxy 的 /config、/data | 入口代理配置与 ACME 证书数据 |
六、环境变量表(部署层核心子集)
plane.env 全量约 40 个变量,下表为部署必看的分层子集(完整逐项解释见《Plane 配置参数详解》):
| 变量 | 实测取值 | 作用 |
|---|---|---|
| APP_RELEASE | v1.4.2 | 全部服务统一版本标签 |
| APP_DOMAIN | localhost | 入口域名(证书与入口路由用) |
| LISTEN_HTTP_PORT / LISTEN_HTTPS_PORT | 8145 / 8146 | 入口监听端口 |
| WEB_URL | http://localhost:8145 | 对外站点地址(邮件链接、CORS 基准) |
| CORS_ALLOWED_ORIGINS | http://localhost:8145 | 允许的跨域来源 |
| SECRET_KEY | 64 位 hex | Django 会话/加密基准密钥 |
| LIVE_SERVER_SECRET_KEY | 64 位 hex | 实时协作服务与后端的共享密钥 |
| PGHOST/PGDATABASE/POSTGRES_USER/POSTGRES_PASSWORD | plane-db/plane/plane/plane | 数据库连接 |
| REDIS_HOST/REDIS_PORT | plane-redis/6379 | 缓存连接 |
| RABBITMQ_HOST/RABBITMQ_USER/RABBITMQ_PASSWORD/RABBITMQ_VHOST | plane-mq/plane/plane/plane | 队列连接 |
| USE_MINIO | 1 | 启用内置对象存储 |
| AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY | access-key/secret-key | 对象存储凭证(同时是 MinIO 根凭证) |
| AWS_S3_ENDPOINT_URL/AWS_S3_BUCKET_NAME | http://plane-minio:9000/uploads | 对象存储端点与桶 |
| FILE_SIZE_LIMIT | 5242880 | 单附件上限(字节,默认 5 MB) |
| GUNICORN_WORKERS | 1 | API 进程数 |
| WEB_REPLICAS 等 7 个 | 1 | 各服务副本数 |
七、常见问题(排障实录)
Q1:up -d 中途被打断,四个服务停在 Created 状态怎么办?
实测本次部署中 proxy/space/live/admin 曾停留在 Created(up 过程被中断,未真正启动)。docker compose ps 里状态为 Created 而非 Up,且宿主端口未监听。处理:在原目录重新执行一次 up -d,Compose 会按依赖关系把缺的服务补启动(实测回显见 4.4 节「Starting → Started」8 行),无需删除重建。
Q2:默认 80/443 端口冲突或不想用特权端口?
改 plane.env 的 LISTEN_HTTP_PORT/LISTEN_HTTPS_PORT(本文 8145/8146),同时 WEB_URL、CORS_ALLOWED_ORIGINS 必须带上同一端口,否则页面能开但 API 跨域被拒。
Q3:更换 SECRET_KEY 后所有登录会话失效(实测)。
改 SECRET_KEY 后执行 up -d 重建后端服务,旧会话 cookie 全部失效:改前 /api/users/me/ 返回 200,改后实测返回 401,重新登录后恢复 200。生产更换该密钥要预期全员掉线,并安排在维护窗口。
Q4:接口注册实例管理员返回 500?
实测两个原因:一是密码未过 zxcvbn 校验(302 到 PASSWORD_TOO_WEAK,见 4.6 节);二是表单里 is_telemetry_enabled 传了小写 false 字符串——Django 5.2 布尔字段只接受 True/False(首字母大写)或布尔值,实测传 false 会 500,传 True 成功。用浏览器引导页则无此问题。
Q5:v1 附件接口 POST .../issues/<id>/issue-attachments/ 返回 500?
实测 v1.4.2 该端点存在已知缺陷:服务端日志记录 'S3Storage' object has no attribute 'file_overwrite'(异常由 exception_logger 记录后返回 500)。官方新前端已切换到 v2 预签名直传流程(4.8 节实测可用),脚本对接请直接走 v2 三步流程,不要用 v1 端点。
Q6:预签名直传返回 400 EntityTooLarge?
预签名策略里含 content-length-range 条件,锁定为登记时声明的 size。实测声明 41 字节而文件实际 42 字节即被 MinIO 拒绝(Your proposed upload exceeds the maximum allowed object size.)。登记前用 wc -c 取准确字节数即可。
Q7:服务都 Up 但页面打不开/curl 不通?
检查入口 proxy 服务是否真的 Up(它承载 8145/8146 发布端口)。若 ps 显示 Created,按 Q1 补启动;再核对本机 ss -ltn 是否有其他进程抢占了目标端口。
八、参考链接
- 官方自托管总览(Compose 与 k8s 双通道说明):https://developers.plane.so/self-hosting/overview(2026-10-27 查询)
- 官方 Kubernetes(Helm)部署文档:https://developers.plane.so/self-hosting/methods/kubernetes(2026-10-27 查询)
- 官方安装器与编排文件来源:https://github.com/makeplane/plane/releases/download/v1.4.2/docker-compose.yml 与
variables.env(2026-10-27 查询) - GitHub 仓库:https://github.com/makeplane/plane(v1.4.2 Releases,2026-10-27 核实)
- 本专栏认知篇:《Plane 介绍与选型指南》
- 配置变量逐项详解:《Plane 配置参数详解》
- 命令行运维与备份实测:《Plane 常用 CLI 命令与运维实战》