Plane Docker Compose 部署实战

Plane 社区版 v1.4.2 官方 Compose 通道部署实测:setup.sh 获取编排、plane.env 定制(端口/密钥)、13 服务逐一起停与状态核验、实例初始化、工作区/项目/Issue/周期/模块创建、MinIO 附件预签名直传验证、官方 k8s(Helm)形态溯源与 7 条排障实录(2026-10-27)。

PlanePlane项目管理Docker2026/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.yaml
  • https://github.com/makeplane/plane/releases/download/<版本号>/variables.env → 落为 plane-app/plane.env

v1.4.2 编排文件定义了 13 个服务,全部走官方 makeplane/* 系列镜像(标签统一受 APP_RELEASE 变量控制):

服务名镜像角色是否必须
webmakeplane/plane-frontend工作台前端(Next.js)是
spacemakeplane/plane-space对外发布页/站点前端是
adminmakeplane/plane-admin实例管理端(God Mode)前端是
livemakeplane/plane-live实时协作 WebSocket 服务是
apimakeplane/plane-backendDjango API 主服务(gunicorn)是
workermakeplane/plane-backendCelery 异步任务是
beat-workermakeplane/plane-backendCelery 周期调度是
migratormakeplane/plane-backend启动期数据库迁移(一次性,跑完即退出)是
plane-dbpostgres:15.7-alpine业务数据库是
plane-redisvalkey/valkey:7.2.11-alpine缓存/队列中间件是
plane-mqrabbitmq:3.13.6-management-alpineCelery 消息队列是
plane-miniodocker.io/pgsty/minio:RELEASE.2026-08-04T00-00-00ZS3 兼容对象存储(附件)是(也可接外部 S3)
proxymakeplane/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 配置篇)。

三、前置条件

  1. 服务器已安装 Docker Engine 与 Compose v2 插件:
docker --version && docker compose version
# Docker version 29.9.2, build a82e528
# Docker Compose version v5.6.1
  1. 规划端口未被占用(官方默认 80/443,本文改用 8145/8146):
ss -ltn | grep -E ':(8145|8146)\b' || echo "端口空闲"
# 端口空闲
  1. 磁盘空间:本次部署拉取完全套服务镜像后,Plane 栈占用约 4 GB;数据卷另计。

  2. 网络说明:Docker Hub 拉取 makeplane/* 与基础镜像在国内网络环境下较慢时,本环境已配置 Docker 拉取代理 192.168.1.62:7897,本次全部 13 个服务镜像均经代理直拉成功,无需手动 -x 降级(⚠️ 实测可用渠道:代理直拉;未测试离线导入路径)。

  3. 若使用自定义网段,先核对 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 存储类),并用 nginx Ingress 暴露入口;生产建议按官方「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 也不会删卷:

卷名挂载点内容
pgdataplane-db 的 /var/lib/postgresql/data全部业务数据(工作区/项目/工作项…)
redisdataplane-redis 的 /data缓存
rabbitmq_dataplane-mq 的 /var/lib/rabbitmq队列持久化
uploadsplane-minio 的 /export附件对象存储
logs_api / logs_worker / logs_beat-worker / logs_migrator各后端服务的 /code/plane/logs服务日志文件
proxy_config / proxy_dataproxy 的 /config、/data入口代理配置与 ACME 证书数据

六、环境变量表(部署层核心子集)

plane.env 全量约 40 个变量,下表为部署必看的分层子集(完整逐项解释见《Plane 配置参数详解》):

变量实测取值作用
APP_RELEASEv1.4.2全部服务统一版本标签
APP_DOMAINlocalhost入口域名(证书与入口路由用)
LISTEN_HTTP_PORT / LISTEN_HTTPS_PORT8145 / 8146入口监听端口
WEB_URLhttp://localhost:8145对外站点地址(邮件链接、CORS 基准)
CORS_ALLOWED_ORIGINShttp://localhost:8145允许的跨域来源
SECRET_KEY64 位 hexDjango 会话/加密基准密钥
LIVE_SERVER_SECRET_KEY64 位 hex实时协作服务与后端的共享密钥
PGHOST/PGDATABASE/POSTGRES_USER/POSTGRES_PASSWORDplane-db/plane/plane/plane数据库连接
REDIS_HOST/REDIS_PORTplane-redis/6379缓存连接
RABBITMQ_HOST/RABBITMQ_USER/RABBITMQ_PASSWORD/RABBITMQ_VHOSTplane-mq/plane/plane/plane队列连接
USE_MINIO1启用内置对象存储
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYaccess-key/secret-key对象存储凭证(同时是 MinIO 根凭证)
AWS_S3_ENDPOINT_URL/AWS_S3_BUCKET_NAMEhttp://plane-minio:9000/uploads对象存储端点与桶
FILE_SIZE_LIMIT5242880单附件上限(字节,默认 5 MB)
GUNICORN_WORKERS1API 进程数
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 是否有其他进程抢占了目标端口。

八、参考链接