Penpot 常用命令与运维实战
Penpot 2.18.5 常用命令与运维实战:8 服务栈运维命令矩阵、单服务重启与开机自愈案例(admin-console 抢启实录)、pg_dump+双数据卷备份实测(181KB dump 与资产分片目录)、升级路径与版本核对、数据库删除保护/导出接口 transit 编码/内存水位等真实排障沉淀(2026-10-28 实测)。
Penpot 常用命令与运维实战
目标:给 Penpot 2.18.5 自托管管理员一套拿来即用的运维手册——栈级命令矩阵、单服务处置、备份恢复实测、升级路径,以及本次真实运维中沉淀的排障实录(开机抢启、数据库删除保护、导出接口编码、内存水位)。部署与配置背景分别见 《Penpot Docker Compose 部署实战》 与 《Penpot 配置参数详解》。以下全部命令与回显为 2026-10-28 真实服务器实测。
一、环境信息
- 栈:Penpot 2.18.5 官方 Compose 编排,项目名
penpot-2.18.5-stack(compose 归一化前缀penpot-218-stack),部署目录/opt/penpot/deploy/,入口:8140。 - 8 服务:frontend / backend / exporter / admin-console / mcp / postgres:15 / valkey:8.1 / mailpit:v1。
- 数据落点:
penpot-218-stack_penpot_postgres_v15(数据库卷)与penpot-218-stack_penpot_assets(资产卷)两个 Docker 卷。
二、栈运维命令矩阵
统一在部署目录执行(cd /opt/penpot/deploy):
| 场景 | 命令 | 说明 |
|---|---|---|
| 全栈状态 | docker compose ps | 8 服务逐一显示 Up/healthy;postgres 与 valkey 自带健康检查 |
| 全栈日志 | docker compose logs -f --tail=100 | 排障第一入口 |
| 单服务日志 | docker compose logs -f penpot-backend | backend/exporter 日志信息量最大 |
| 资源水位 | docker stats --no-stream | 内存/CPU 快照 |
| 重启单服务 | docker compose restart penpot-backend | 不重建、不重读编排文件 |
| 重建单服务 | docker compose up -d penpot-backend | 编排/.env 变更后使用(可重读配置) |
| 停栈 | docker compose stop | 保留数据卷 |
| 起栈 | docker compose start | 配合 stop 做计划内停机 |
| 销毁栈 | docker compose down | 保留卷;down -v 连卷一起删(数据全失,见第七章) |
| 数据库直连 | docker compose exec penpot-postgres psql -U penpot penpot | 只读排查用 |
版本核对的两个实测入口:
docker compose logs penpot-backend | grep "welcome to penpot" # → version="2.18.5-RC1"
docker compose ps # → penpotapp/backend:2.18.5
💡 技术拓展:
version="2.18.5-RC1"不是拿错版本——GitHub tags 实查(2026-10-28)显示2.18.5与2.18.5-RC1指向同一提交f4456696,正式版与 RC1 是同一构建,日志串与镜像 tag 的写法差异而已。
三、单服务处置实录:开机抢启与自愈
3.1 真实案例
2026-10-28 00:34 宿主机重启,全栈依赖 restart: always 自动拉起。admin-console(Node 服务)抢在 PostgreSQL 完成启动前发起连接,当场报错退出(真实日志节选):
penpot-admin-console-1 | 2026-10-28T00:34:21.596498161Z throw new DrizzleQueryError(queryString, params, e);
penpot-admin-console-1 | DrizzleQueryError: Failed query: CREATE SCHEMA IF NOT EXISTS "drizzle"
penpot-admin-console-1 | cause: error: the database system is starting up
几秒后自动重试成功(它自己在数据库侧建 drizzle 迁移 schema),服务恢复,无需人工干预。结论:抢启失败是自愈型瞬时错误,看到 the database system is starting up 不必动手;只有持续 1 分钟以上不恢复才需要 docker compose restart penpot-admin-console。backend/exporter 因有 service_healthy 依赖门控不会抢启,这是官方编排的健康检查设计(2 秒间隔×5 次)。
3.2 重启后必看的三行日志
每次整栈重启后按序确认:
penpot-postgres-1 | LOG: database system is ready to accept connections
penpot-backend-1 | I app.migrations - hint="running migrations", module=:app.migrations/migrations
penpot-backend-1 | I app.main - hint="welcome to penpot", flags="...", version="2.18.5-RC1"
backend 每次启动都会重跑一遍迁移检查(幂等),welcome to penpot 出现即代表 API(内部 6060 端口)就绪。
四、备份与恢复实测
4.1 备份对象盘点
docker volume ls --format '{{.Name}}' | grep penpot
penpot-218-stack_penpot_assets
penpot-218-stack_penpot_postgres_v15
两个数据卷分别承载:PostgreSQL 15 数据目录(用户/团队/文件元数据)与资产目录(用户上传图片、导出产物,按资产 id 前两位分片存放,实测目录形态为 99/、ee/)。测试邮箱 mailpit 的消息仅存内存,官方注释明确不落盘、重建即丢——不需要也无处备份,生产接真实 SMTP 后此服务可弃用。
4.2 数据库备份(pg_dump 实测)
cd /opt/penpot/deploy
docker compose exec penpot-postgres pg_dump -U penpot penpot > /opt/backup/penpot-$(date +%F).sql
实测产物:181082 字节(空库+测试数据状态),包含全部建表与数据。建议每日 cron 执行并轮转保留 7~30 天。
4.3 资产卷备份
docker run --rm -v penpot-218-stack_penpot_assets:/data -v /opt/backup:/backup alpine \
tar czf /backup/penpot-assets-$(date +%F).tar.gz -C /data .
初始实测仅 36KB(两个分片目录),随上传量增长;若已把对象存储切到 S3/MinIO(配置见 《Penpot 配置参数详解》),此卷备份改为对应桶的同步策略。
4.4 恢复顺序
- 起一个全新栈(同版本
PENPOT_VERSION,先不对外开放); - 停 backend/admin-console(
docker compose stop penpot-backend penpot-admin-console),避免恢复期间写入; docker compose exec -T penpot-postgres psql -U penpot penpot < penpot-YYYY-MM-DD.sql回灌数据库;- 资产卷解包回
penpot_assets卷对应位置; - 起回停掉的服务,登录验证文件可打开、图片资产可加载。
五、升级路径
官方建议小步快跑(small increments),实测标准动作:
cd /opt/penpot/deploy
vi .env # PENPOT_VERSION=2.19.0(示例)
docker compose pull # 拉取新版本
docker compose up -d # 滚动重建
docker compose logs penpot-backend | grep "welcome to penpot" # 核对新 version
升级前强制项:先做第四章备份(数据库卷 + 资产卷);迁移在启动时自动执行且只进不退,跨大版本(如 2.18→2.20)无法用同一份数据降级回滚——回滚等于恢复备份。
六、排障实录沉淀
6.1 数据库删除保护(最意外的发现)
直接用 SQL 删除业务行会被拒绝(实测回显):
ERROR: unable to proceed to delete row on "team"
HINT: disable deletion protection with "SET rules.deletion_protection TO off"
CONTEXT: PL/pgSQL function raise_deletion_protection() line 3 at RAISE
Penpot 在 PostgreSQL 内装了触发器级的数据删除保护(覆盖 team/project/file 等核心表),防止误删。确需手工清理时(如删除测试账号残留),必须在事务内显式关闭保护:
BEGIN;
SET LOCAL rules.deletion_protection TO off;
DELETE FROM public.team WHERE id = '<team-id>';
COMMIT;
两个细节:SET LOCAL 作用域仅当前事务,提交后自动恢复保护;表名是单数(profile/team/file/project),与直觉的复数命名不同。
6.2 导出接口必须用 transit 编码
对 /api/export(frontend 统一入口转发 exporter)做脚本化导出时,普通 JSON 体看似合法却报内部错误:
HTTP 500
{"~:type":"~:server-error","~:hint":"conj on a map takes map entries or seqables of map entries"}
原因是服务端按 transit 协议解析参数:键名与枚举值需带 ~: 前缀、UUID 需 ~u 前缀、Content-Type 用 application/transit+json。改对后即 200 并返回资产下载地址(完整可用的导出脚本见 《Penpot Docker Compose 部署实战》第五章)。参数校验失败时服务端会返回带 spec 说明的 400,把缺失/错误的键名原文列出,是调试导出参数最有效的反馈。
6.3 导出 503 与临时文件
- 导出服务首启或重启后,内置无头浏览器需要数秒就绪;此窗口内的导出请求会得到 503(服务端错误码
browser-not-ready),等待重试即可,无需重启。 - 渲染产物先落在
/tmp/penpot/penpot.tmp.bitmap.*.png,带定时删除(实测日志hint="schedule tempfile deletion")。若/tmp被按会话清空不影响正确性——临时文件本就一次性。 - exporter 空闲即占 461MiB(无头浏览器常驻),并发导出会继续上涨;内存紧张时优先调低
PENPOT_EXPORTER_MAX_CONCURRENT_JOBS(默认 4)。
6.4 内存水位基线(实测)
| 服务 | 空闲常驻 | 建议规划值 |
|---|---|---|
| penpot-backend | 2.1 GiB | 2.5 GiB(已加 -Xmx512m 仍需此量,堆外占大头) |
| penpot-exporter | 461.7 MiB | 1 GiB(含并发余量) |
| penpot-mcp | 190.6 MiB | 256 MiB |
| penpot-frontend | 172.9 MiB | 256 MiB |
| penpot-postgres | 167.7 MiB | 512 MiB |
| penpot-admin-console | 93.1 MiB | 128 MiB |
| penpot-mailpit | 32.5 MiB | 64 MiB |
| penpot-valkey | 4.2 MiB | 128 MiB 上限(VALKEY_EXTRA_FLAGS 限定) |
全栈合计约 3.2GiB 空闲水位;2GB 服务器跑不动,4GB 起步,6GB 从容。
6.5 其他实测备忘
GET /api/healthz返回 404——Penpot 没有这个习惯路径的探活端点;服务健康判断用docker compose ps的 healthy 状态 + 入口 HTTP 200。- admin-console 日志里的
appVersion显示2.18.0-RC4-1-gd8dd1b8,与其余服务的2.18.5-RC1不一致——管理控制台组件有自己的构建版本线,同栈混显属正常。 - 数据库直查用户清单:
docker compose exec penpot-postgres psql -U penpot -c "select email, fullname from public.profile;"(注册/登录异常时核对账号是否存在)。 - 弱密码直接被服务端策略拒绝(400 weak-password,要求大小写+数字+特殊字符全齐),不是可配置项,见 《Penpot Docker Compose 部署实战》4.1 实测。
七、管理后台与辅助工具
| 工具 | 入口 | 用途与注意 |
|---|---|---|
| Admin Console | http://<服务器IP>:8140/admin-console/ | 实例级用户/团队/项目管理(2.18 新增),依赖 enable-admin-console 旗标 |
| Mailpit | http://127.0.0.1:8148(默认编排为 :8025,本例改绑回环) | 测试邮箱收件箱,查看注册/邀请邮件内容;消息仅存内存 |
| MCP | /mcp/ws、/mcp/stream(同端口) | AI 智能体接入通道,依赖 enable-mcp 旗标 |
| PREPL | backend 旗标 enable-prepl-server(默认开) | 运维诊断 REPL,官方默认保留 |
八、停栈与数据安全
docker compose down # 停栈并移除编排网络,两个数据卷保留
docker compose down -v # ⚠️ 连卷一起删除:数据库与资产全部清空,不可恢复
down -v 只应用于测试栈重置或确认备份完整后的下线。生产栈日常停机一律 stop/start 或不带 -v 的 down。