Figma 涨价、设计稿还在外网托管用 Penpot 搭一套自托管设计工作台再用 cpolar 把评审入口开给甲方上周一个做外包的朋友跟我吐槽甲方突然要他把两年前的项目设计稿调出来改一版结果打开 Figma 一看免费团队的编辑权限掉了历史文件变成了只读重新拉人又得再走一遍席位申请。更尴尬的是稿子从头到尾都在别人的服务器上他自己连源文件都导不全。设计稿这东西本质上是团队的核心资产。放在别人的 SaaS 里价格涨不涨、席位够不够、导出格式给不给全都不是你说了算。所以我干脆帮他换了一套思路设计工作台自托管评审入口单独开给甲方看。这篇就把整套流程写清楚。本地用 Docker 起 Penpot甲方看不到你的内网怎么办以及怎么用 cpolar 把评审链接稳定地开出去。1 什么是 Penpot这篇里它负责什么Penpot 是一套开源的设计与原型工具可以在浏览器里画界面、做原型、管理设计系统。它和 Figma 最像的地方是协作方式——多人同时打开同一张画板光标位置实时可见不同的地方是Penpot 可以完全部署在你自己的服务器上设计稿存在你自己的数据库和存储里。放在这篇的场景里Penpot 负责的是设计和评审的载体具体做三件事团队成员在自己服务器上画稿、改稿文件不出内网甲方通过一个链接直接打开某张画板看效果、留评论不需要注册账号所有版本历史、素材资源都存在本地导出格式自己说了算它由几个容器组成但这篇里你只需要记住两个对外入口frontend 的9001是你和团队日常打开的地址backend 的6060是给前端和外部调用提供接口的服务。后面接 cpolar 的时候就是把这两个入口安全地开出去。图1Penpot 自托管架构示意frontend(9001) backend(6060) exporter 内部协作2 环境准备装 Docker确认端口不打架Penpot 官方推荐的部署方式是 Docker Compose所以先把环境备好。硬件方面别太抠。Penpot 官方 Compose 文件里同时跑着 frontend、backend、exporter、postgres、valkey 好几个容器官方建议至少给到双核 2GHz 以上、4GB 内存起。如果只是几个人小团队用4GB 能跑起来要是团队规模大一点建议直接给到 8GB浏览器端渲染大画板的时候会顺很多。系统用常见的 amd64 Linux 发行版就行Ubuntu 22.04 最省事。2.1 安装 Docker 和 Compose如果机器上还没 Docker用官方脚本装curl -fsSL https://get.docker.com | sudo sh sudo systemctl enable --now docker docker compose version最后那条命令如果输出了版本号说明 Compose V2 已经装好了。如果报docker: compose is not a docker command再单独装一次 compose-plugin。2.2 确认 9001 和 6060 端口空着Penpot 默认把 frontend 映射到宿主机的9001backend 走内部网络。先看一眼这两个端口有没有被占sudo ss -tlnp | grep -E :9001|:6060敲完没有任何输出说明端口都空着可以放心往下走。如果 9001 被占了把后面的端口映射换成 9002 之类就行记住要改两处Compose 文件里的端口映射以及 cpolar 隧道的目标端口。3 用 docker compose 部署 Penpot先建一个目录把所有东西收在一起以后备份和升级都好找。sudo mkdir -p /opt/penpot cd /opt/penpot接着拉官方提供的 Compose 文件不要自己手写官方那份已经把各个容器的依赖关系、健康检查都配好了curl -o docker-compose.yaml https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml拉下来之后先别急着启动检查两处最容易出问题的地方。第一处是PENPOT_PUBLIC_URI。这个变量决定了前端和后端互相之间、以及浏览器里打开时用的地址。官方示例里默认写的是x-uri: penpot-public-uri PENPOT_PUBLIC_URI: http://localhost:9001如果你打算直接在局域网里用 IP 访问把这里改成http://你的服务器IP:9001。这一步现在先填局域网 IP别一上来就填公网地址——先把内网链路打通公网那段单独调出问题的时候你才能分清是配置错了还是隧道错了。第二处是PENPOT_SECRET_KEY。官方示例里默认是change-this-insecure-key这个必须换掉。它相当于整个实例的密钥种子用来派生会话和邀请令牌。生成一个随机值python3 -c import secrets; print(secrets.token_urlsafe(64))把输出的那串字符串填进 Compose 文件里所有出现PENPOT_SECRET_KEY的位置。敲黑板这个值一旦定了就别再改改掉之后已登录的会话会全部失效。改完检查一下配置文件语法然后启动docker compose -p penpot -f docker-compose.yaml config /dev/null echo 配置语法正确 docker compose -p penpot -f docker-compose.yaml up -d第一次启动要初始化 Postgres 数据库等一两分钟。然后用下面这条命令验证curl -I http://127.0.0.1:9001返回200或302都说明 frontend 起来了。浏览器打开http://服务器IP:9001能看到 Penpot 的注册/登录界面这一步就算通了。如果卡在Connection refused按这个顺序查容器是不是在Restarting用docker compose -p penpot -f docker-compose.yaml ps看状态、docker compose -p penpot -f docker-compose.yaml logs penpot-backend看日志、内存是不是不够、9001 是不是真被占了。图2docker compose ps 显示 penpot-frontend、penpot-backend、postgres、valkey 等容器全部 Up4 创建第一个账号把设计稿跑起来Penpot 默认开启了注册。打开http://服务器IP:9001点注册填邮箱和密码。如果默认配置里开了邮箱验证注册后要去邮箱点确认链接。这里有个小细节官方 Compose 文件里带了一个penpot-mailcatch容器用来临时接住 Penpot 发出的邮件免得你还没配 SMTP 就卡在验证环节。如果你没配真实 SMTP可以去http://服务器IP:1080直接看邮件内容把验证链接捞出来。注意官方 Compose 文件的注释里明确提醒过如果要把 Penpot 暴露到公网应该去掉disable-email-verification和disable-secure-session-cookies这两个 flag。默认文件里为了本地体验是开着的后面要正式给甲方用的时候记得处理这个在第 6 节会说。注册登录之后新建一个项目随便画几笔或者拖几个素材进去。能在画板里正常操作、刷新之后稿子还在说明前端、后端、数据库三者已经打通了。图3Penpot 画布界面左侧图层、中间画板、右侧属性面板到这一步局域网内的团队协作已经能用了。成员各自注册账号、被拉进同一个项目就可以一起改稿。5 甲方看不到内网怎么办用 cpolar 开一条 HTTPS 隧道现在这套 Penpot 只有一个问题它只在内网可见。甲方在另一个城市浏览器根本访问不到你的192.168.x.x。给评审开入口这件事传统做法是给甲方单独搭一套远程接入通道但那种方案既要装客户端、又要发账号甲方那边往往嫌麻烦。更轻的做法是给 Penpot 的9001端口做一条公网映射让甲方直接点链接就能看。这一步用 cpolar 来做。5.1 安装 cpolarLinux 环境下一条命令搞定curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash装完检查服务状态cpolar version curl -s http://127.0.0.1:9200 /dev/null echo Web UI 正常9200是 cpolar 的本地 Web 管理界面端口能打开就说明服务正常。5.2 绑定账号浏览器打开http://127.0.0.1:9200用 cpolar 账号登录。多数情况下登录完成后authtoken 会自动写进配置文件。如果是纯命令行机器不方便开图形界面去后台手动拿 token登录https://dashboard.cpolar.com/login点顶部验证进入https://dashboard.cpolar.com/auth页面上你的隧道 Authtoken下面的那串就是。拿到后执行cpolar authtoken 你复制的那串token5.3 创建 HTTP 隧道指向 9001cpolar http 9001命令跑起来后终端会打出一个https://xxxx.cpolar.cn的公网地址。这个地址就是甲方打开设计稿的入口。本地服务端口记得换成你自己的实际值别照着抄。先自己用手机流量不要连 WiFi打开这个地址测一次。能加载出 Penpot 登录页说明隧道通了。如果打不开按这个顺序查本地127.0.0.1:9001能不能访问、9200里隧道是不是在线、公网地址有没有打错。5.4 把公网地址回填给 Penpot这一步是整套方案里最容易漏掉、但最关键的一步。Penpot 的 frontend 和 backend 之间要靠PENPOT_PUBLIC_URI互相定位。如果你只在浏览器里用公网地址访问但配置里还写着局域网 IP会出现页面能打开、但登录和保存失效的怪现象——因为浏览器发出去的请求地址和实例自己认为的地址对不上。所以当你要正式把公网入口给甲方用时需要把 Compose 文件里的PENPOT_PUBLIC_URI改成 cpolar 给的 HTTPS 地址x-uri: penpot-public-uri PENPOT_PUBLIC_URI: https://xxxx.cpolar.cn改完之后重建容器让配置生效只重启不够环境变量变更需要重新创建容器docker compose -p penpot -f docker-compose.yaml up -d现在甲方就能通过这个地址打开设计稿、留评论了。提醒一句公开评审链接之前先想清楚开哪一级权限。Penpot 里可以给分享链接设置查看或编辑权限给甲方看稿默认给查看就够别手滑给了编辑。另外正式对外用之前记得按官方提醒去掉disable-email-verification和disable-secure-session-cookies这两个 flag同时给你的团队账号都设上强密码。6 固定二级子域名别让评审链接三天两头变上一步用的是 cpolar 免费套餐的随机地址有个很实际的麻烦——这种随机公网地址 24 小时内会变化。地址一变你发给甲方的链接就失效了PENPOT_PUBLIC_URI也得跟着改一遍、重建一次容器。内部小工具还好说但这是给外部甲方的评审入口链接隔天就打不开观感很差。解决办法是用固定二级子域名需要基础套餐或以上。配好之后你的评审入口就固定成类似https://design.cpolar.cn这样的地址写进PENPOT_PUBLIC_URI之后不用再动发给甲方的链接也一直有效。这里补充一个顺序上的建议正式给甲方之前先在 cpolar 里把固定域名配好再把它填进 Penpot 的配置。这样只需要重建一次容器少折腾一轮。如果后面还想用自己的域名当入口那需要专业套餐或以上按需选就行。7 总结到这里一套自己能掌控的设计工作台就跑起来了。设计稿存在你自己的服务器上成员在你自己的实例里协作甲方通过一个稳定的公网地址就能看稿、留意见不用注册、不用装客户端。回顾一下几个关键动作用官方docker-compose.yaml起了整套 Penpotfrontend 对外走9001改掉默认的PENPOT_PUBLIC_URI和PENPOT_SECRET_KEY先局域网验证再用公网用 cpolar 给9001端口开 HTTPS 隧道把公网地址回填进PENPOT_PUBLIC_URI解决登录保存失效的问题上固定二级子域名让评审链接长期稳定不用每次重发这套方案的好处是可扩展以后要加人就在 Penpot 里建账号或者发邀请要加机器就把 Compose 文件复制一份改端口。入口地址始终是那个固定域名团队规模怎么变都不用重新配一遍。下次甲方说设计稿打不开你只需要把那条链接再发一次就行。