Figma 涨价、设计稿还在外网托管?用 Penpot 搭一套自托管设计工作台,再用 cpolar 把评审入口开给甲方
上周一个做外包的朋友跟我吐槽,甲方突然要他把两年前的项目设计稿调出来改一版,结果打开 Figma 一看,免费团队的编辑权限掉了,历史文件变成了只读,重新拉人又得再走一遍席位申请。更尴尬的是,稿子从头到尾都在别人的服务器上,他自己连源文件都导不全。
设计稿这东西,本质上是团队的核心资产。放在别人的 SaaS 里,价格涨不涨、席位够不够、导出格式给不给全,都不是你说了算。所以我干脆帮他换了一套思路:设计工作台自托管,评审入口单独开给甲方看。
这篇就把整套流程写清楚。本地用 Docker 起 Penpot,甲方看不到你的内网怎么办,以及怎么用 cpolar 把评审链接稳定地开出去。
1 什么是 Penpot,这篇里它负责什么
Penpot 是一套开源的设计与原型工具,可以在浏览器里画界面、做原型、管理设计系统。它和 Figma 最像的地方是协作方式——多人同时打开同一张画板,光标位置实时可见;不同的地方是,Penpot 可以完全部署在你自己的服务器上,设计稿存在你自己的数据库和存储里。
放在这篇的场景里,Penpot 负责的是"设计和评审的载体",具体做三件事:
- 团队成员在自己服务器上画稿、改稿,文件不出内网
- 甲方通过一个链接直接打开某张画板,看效果、留评论,不需要注册账号
- 所有版本历史、素材资源都存在本地,导出格式自己说了算
它由几个容器组成,但这篇里你只需要记住两个对外入口:frontend 的9001是你和团队日常打开的地址,backend 的6060是给前端和外部调用提供接口的服务。后面接 cpolar 的时候,就是把这两个入口安全地开出去。
图1:Penpot 自托管架构示意,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 映射到宿主机的9001,backend 走内部网络。先看一眼这两个端口有没有被占:
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 是不是真被占了。
图2:docker compose ps 显示 penpot-frontend、penpot-backend、postgres、valkey 等容器全部 Up
4 创建第一个账号,把设计稿跑起来
Penpot 默认开启了注册。打开http://服务器IP:9001,点注册,填邮箱和密码。如果默认配置里开了邮箱验证,注册后要去邮箱点确认链接。
这里有个小细节:官方 Compose 文件里带了一个penpot-mailcatch容器,用来临时接住 Penpot 发出的邮件,免得你还没配 SMTP 就卡在验证环节。如果你没配真实 SMTP,可以去http://服务器IP:1080直接看邮件内容,把验证链接捞出来。
注意,官方 Compose 文件的注释里明确提醒过:如果要把 Penpot 暴露到公网,应该去掉disable-email-verification和disable-secure-session-cookies这两个 flag。默认文件里为了本地体验是开着的,后面要正式给甲方用的时候记得处理,这个在第 6 节会说。
注册登录之后,新建一个项目,随便画几笔或者拖几个素材进去。能在画板里正常操作、刷新之后稿子还在,说明前端、后端、数据库三者已经打通了。
图3:Penpot 画布界面,左侧图层、中间画板、右侧属性面板
到这一步,局域网内的团队协作已经能用了。成员各自注册账号、被拉进同一个项目,就可以一起改稿。
5 甲方看不到内网怎么办:用 cpolar 开一条 HTTPS 隧道
现在这套 Penpot 只有一个问题:它只在内网可见。甲方在另一个城市,浏览器根本访问不到你的192.168.x.x。
给评审开入口这件事,传统做法是给甲方单独搭一套远程接入通道,但那种方案既要装客户端、又要发账号,甲方那边往往嫌麻烦。更轻的做法是给 Penpot 的9001端口做一条公网映射,让甲方直接点链接就能看。
这一步用 cpolar 来做。
5.1 安装 cpolar
Linux 环境下一条命令搞定:
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 隧道,指向 9001
cpolar 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起了整套 Penpot,frontend 对外走9001 - 改掉默认的
PENPOT_PUBLIC_URI和PENPOT_SECRET_KEY,先局域网验证再用公网 - 用 cpolar 给
9001端口开 HTTPS 隧道,把公网地址回填进PENPOT_PUBLIC_URI解决登录保存失效的问题 - 上固定二级子域名,让评审链接长期稳定,不用每次重发
这套方案的好处是可扩展:以后要加人,就在 Penpot 里建账号或者发邀请;要加机器,就把 Compose 文件复制一份改端口。入口地址始终是那个固定域名,团队规模怎么变都不用重新配一遍。下次甲方说设计稿打不开,你只需要把那条链接再发一次就行。