Documenso Docker Compose 部署实战
Documenso v2.21.1 社区版生产编排实测:自签签署证书、官方 Compose 双服务基线(应用 + PostgreSQL + Mailpit)、首启迁移日志走读、管理员账号初始化,沉淀证书挂载权限、URL 体系、SMTP 三类真实排障与数据库备份恢复。
Documenso Docker Compose 部署实战
Documenso 官方为社区版提供生产级 Docker Compose 编排(docker/production/compose.yml),以「应用 + PostgreSQL」双服务为骨架,数据库迁移在应用启动时自动执行。本文基于 2026 年 10 月 28 日实查的官方最新稳定版 v2.21.1,在一台 Docker 宿主机上完整走通:自签签署证书 → 编写编排文件 → 首次启动 → 管理员账号与邮件验证 → 数据备份,并把实测踩到的三个坑(密码策略、签署证书挂载权限、URL 体系不一致)原样沉淀。产品定位与选型背景见《Documenso 介绍与选型指南》,全部环境变量逐一说明见《Documenso 配置参数详解》,日常命令与 API 实测见《Documenso 常用命令与运维实战》。
一、部署前的三件准备
1.1 资源与端口规划
v2.21.1 单实例资源占用很轻(空载应用进程约 300 MB 内存),但签署、封存是 CPU 密集操作,官方建议至少 2 vCPU / 2 GB 内存。本实测环境采用如下规划,后文所有命令均以此为准:
| 项目 | 取值 | 说明 |
|---|---|---|
| 应用端口 | 8130 | 映射到容器内 3000(PORT 环境变量控制容器内端口) |
| Mailpit Web 端口 | 8133 | 映射到容器内 8025,用于接收验证/签署邮件 |
| 数据库 | 无对外端口 | 仅在编排内网暴露 5432 |
| 子网 | 10.89.57.0/24 | 自定义 bridge 网络,避免与宿主机既有网段冲突 |
| 编排名 | documenso-v2.21.1-stack | compose.yaml 中显式 name: 固定 |
端口是否空闲先用 ss -ltn 核对,例如确认 8130 未被占用:
ss -ltn | grep 8130 || echo "8130 空闲"
实测回显(空闲时 grep 无输出,进入分支打印提示):
8130 空闲
1.2 镜像版本确认
官方镜像为 Docker Hub 的 documenso/documenso,v2.21.1 于 2026 年 10 月 22 日发布。拉取前可先确认 tag 存在,再固定版本拉取(不要用 latest,升级时难以回滚):
docker pull documenso/documenso:v2.21.1
docker pull postgres:15
实测回显(节选):
v2.21.1: Pulling from documenso/documenso
Digest: sha256:36190a74d27493d3a03a3ee414589767ba319cde606712a49621e8a5d3730d14
Status: Downloaded newer image for documenso/documenso:v2.21.1
docker.io/documenso/documenso:v2.21.1
1.3 自签签署证书(.p12)
Documenso 完成一个签署回合后,会由后台任务 internal.seal-document 用 PKCS#12 证书对最终 PDF 做数字封存。自托管最小可用方案是用 OpenSSL 自签一张证书并导出为 .p12:
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
-keyout documenso-cert.key -out documenso-cert.crt \
-subj "/CN=Documenso Self Signed"
openssl pkcs12 -export \
-in documenso-cert.crt -inkey documenso-cert.key \
-out cert.p12 -name "Documenso Self Signed" -passout pass:q1w2e3r4
法律效力注记:自签证书仅保证 PDF 的完整性可验证(证明签署后未被篡改),不具备受信任 CA 链。若业务需要合规级签章,官方在 v2.21.x 的
NEXT_PRIVATE_SIGNING_TRANSPORT中提供了gcloud-hsm(Google Cloud HSM 托管密钥)与csc(Cloud Signature Consortium 远程签名,支持 AES/QES 签名级别)两种进阶通道,详见《Documenso 配置参数详解》。
实测踩坑 ①:挂载的 .p12 权限。OpenSSL 生成的文件默认权限是 600(属主 root)。官方编排把它以只读方式挂进容器,但容器内应用进程以非 root 用户运行,导致封存任务读取证书时报 EACCES: permission denied, open '/opt/documenso/cert.p12'(完整排障过程见第四节)。生成证书后记得放开组/其他用户的读权限:
chmod 644 cert.p12
二、编排文件与目录结构
2.1 目录布局
documenso/
├── compose.yaml
└── certs/
└── cert.p12
2.2 compose.yaml 全文
以下编排以官方 docker/production/compose.yml 为基线,固定了镜像版本、宿主端口,并追加了一个 Mailpit 邮件服务用于接收验证邮件与签署回执(生产环境请替换为真实 SMTP):
name: documenso-v2.21.1-stack
services:
database:
image: postgres:15
container_name: documenso-v2.21.1-db
environment:
- POSTGRES_USER=documenso
- POSTGRES_PASSWORD=q1w2e3r4
- POSTGRES_DB=documenso
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U documenso -d documenso']
interval: 10s
timeout: 5s
retries: 5
volumes:
- database:/var/lib/postgresql/data
networks:
- documenso-net
mailpit:
image: axllent/mailpit:v1
container_name: documenso-v2.21.1-mailpit
ports:
- '8133:8025'
networks:
- documenso-net
documenso:
image: documenso/documenso:v2.21.1
container_name: documenso-v2.21.1-web
depends_on:
database:
condition: service_healthy
environment:
- PORT=3000
- NEXTAUTH_SECRET=tetAS1mCOCeZcDcCRqYWuW+xXLhA0OUvDc3GkGmlWaI=
- NEXT_PRIVATE_ENCRYPTION_KEY=18Rzqyosq3F2CFyeSee8MSn5axbGcpfOvCDF8fM6ajM=
- NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=mfqq0CYjsztDZB2tQCCaM/Qgqj/J38HmL5Rj9fBTXek=
- NEXT_PUBLIC_WEBAPP_URL=http://localhost:8130
- NEXT_PRIVATE_INTERNAL_WEBAPP_URL=http://localhost:3000
- NEXT_PRIVATE_DATABASE_URL=postgres://documenso:q1w2e3r4@database:5432/documenso
- NEXT_PRIVATE_DIRECT_DATABASE_URL=postgres://documenso:q1w2e3r4@database:5432/documenso
- NEXT_PUBLIC_UPLOAD_TRANSPORT=database
- NEXT_PRIVATE_SMTP_TRANSPORT=smtp-auth
- NEXT_PRIVATE_SMTP_HOST=mailpit
- NEXT_PRIVATE_SMTP_PORT=1025
- NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS=true
- NEXT_PRIVATE_SMTP_FROM_NAME=Documenso
- NEXT_PRIVATE_SMTP_FROM_ADDRESS=noreply@documenso.local
- NEXT_PRIVATE_SIGNING_TRANSPORT=local
- NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=/opt/documenso/cert.p12
- NEXT_PRIVATE_SIGNING_PASSPHRASE=q1w2e3r4
ports:
- '8130:3000'
volumes:
- ./certs/cert.p12:/opt/documenso/cert.p12:ro
networks:
- documenso-net
volumes:
database:
networks:
documenso-net:
driver: bridge
ipam:
config:
- subnet: 10.89.57.0/24
几个关键点的取值依据:
- 三把密钥必须重新生成。
NEXTAUTH_SECRET(会话签名)与两把NEXT_PRIVATE_ENCRYPTION_*(字段值/令牌对称加密)不要沿用本文示例值,用openssl rand -base64 32各生成一份。加密密钥一旦丢失,已存数据无法解密,务必随数据库一起备份。 - URL 体系一对外一内。
NEXT_PUBLIC_WEBAPP_URL是浏览器看到的地址(本实测是http://localhost:8130),会出现在验证链接、签署链接里;NEXT_PRIVATE_INTERNAL_WEBAPP_URL是服务端回环调用自己的地址(容器内是http://localhost:3000)。两者不一致时,务必只改前者。 - 存储走 database。社区版默认把 PDF 原件、签署后文件全部存 PostgreSQL(
NEXT_PUBLIC_UPLOAD_TRANSPORT=database),因此整个栈只有一个数据卷,备份简化为一件事——备份数据库。大文件量场景再切 S3,见《Documenso 配置参数详解》。 - SMTP 必须可用。注册后的邮箱验证、签署邀请、完成回执都走邮件。Mailpit 只收不发,最适合联调;生产换成真实 SMTP 并配置
NEXT_PRIVATE_SMTP_USERNAME/PASSWORD。
写完后先做语法校验再启动:
docker compose config --quiet && echo COMPOSE-OK
实测回显:
COMPOSE-OK
三、首次启动与账号初始化
3.1 起栈与启动日志走读
docker compose up -d
实测回显:
[+] Running 5/5
✔ Network documenso-v2.21.1-stack_documenso-net Created
✔ Volume "documenso-v2.21.1-stack_database" Created
✔ Container documenso-v2.21.1-mailpit Started
✔ Container documenso-v2.21.1-db Started
✔ Container documenso-v2.21.1-web Started
应用容器启动过程分三步:证书自检 → 数据库迁移 → 启动服务。首次启动日志关键行(实测摘录):
🚀 Starting Documenso...
🔐 Checking certificate configuration...
✅ Certificate file found and readable - document signing is ready!
...
🗄️ Running database migrations...
166 migrations found in prisma/migrations
All migrations have been successfully applied.
🌟 Starting Documenso server...
迁移即上节的 npx prisma migrate deploy,166 个迁移文件一次性建完全部表结构(本实测库共 54 张表)。重启后再看该段日志会变为 No pending migrations to apply.,天然幂等。
3.2 健康检查
官方内置两个无需认证的探活端点,浏览器直接访问即可:
curl -s http://localhost:8130/api/health
curl -s http://localhost:8130/api/certificate-status
实测回显:
{"status":"ok","timestamp":"2026-10-28T06:40:42.132Z","checks":{"database":{"status":"ok"},"certificate":{"status":"ok"}}}
{"isAvailable":true,"timestamp":"2026-10-28T06:40:42.165Z"}
database 与 certificate 双 ok 才算部署完成。浏览器打开 http://localhost:8130/signin 看到 200 即页面可用。
3.3 注册第一个账号
访问 http://localhost:8130/signup 注册。实测踩坑 ②:密码策略。社区版密码校验要求同时包含大写字母与特殊字符,纯小写+数字的常见弱口令会被直接拒绝:
Error
Password must contain at least one uppercase and one special character.
换用满足策略的强密码(本实测用 Q1w2e3r4!,与弱口令仅差大小写与一个感叹号)后注册成功。新账号默认是普通用户,需要进数据库提权为管理员:
docker exec documenso-v2.21.1-db psql -U documenso -d documenso \
-c "UPDATE \"User\" SET roles = '{ADMIN,USER}' WHERE email = 'admin@documenso.local' RETURNING id, email, roles;"
实测回显:
id | email | roles
----+-----------------------+------------
3 | admin@documenso.local | {ADMIN,USER}
(1 row)
UPDATE 1
提权后重新登录该账号即可看到完整的管理菜单。注意注册时的邮箱会收到一封「Please confirm your email」,验证链接指向 NEXT_PUBLIC_WEBAPP_URL 域名,未点击验证前不影响登录,但建议完成以走通全邮件链路。
四、排障实录
坑一:签署证书 EACCES,文档签完卡在 PENDING
现象:用 API 完成签署后,收件人状态已是 SIGNED、管理员也收到了「has signed」邮件,但文档状态一直停在 PENDING,拿不到已封存的签署件。
定位:翻应用日志发现封存任务在重试后失败:
[JOBS]: Triggering job internal.seal-document with payload { documentId: 2, requestMetadata: { userAgent: 'curl/8.5.0' } }
[JOBS:task-5cf8e8c4...] Task failed Error: EACCES: permission denied, open '/opt/documenso/cert.p12'
[JOBS]: Job internal.seal-document failed BackgroundTaskExceededRetriesError: Task exceeded retries
根因:.p12 在宿主机上是 root 属主、600 权限,只读挂载后容器内非 root 的应用进程读不了。启动自检只打印警告不阻断(⚠️ Certificate not found or not readable 分支),所以问题延迟到第一次封存才暴露——这与 start.sh 中「Certificate not found or not readable → document signing will be unavailable」的提示逻辑完全吻合。
修复:宿主机 chmod 644 certs/cert.p12,然后 docker compose restart documenso。重启后用 /api/certificate-status 确认 {"isAvailable":true}。注意:已耗尽重试的那份文档不会被自动补封存,需要重新走一遍签署流程(这也是本文把「修复后再完整签一轮」作为验证动作的原因)。
坑二:URL 体系不一致,验证链接指向错误端口
现象:NEXT_PUBLIC_WEBAPP_URL 若仍保持官方默认 http://localhost:3000,而宿主机映射成了 8130,则注册验证邮件、签署邀请邮件里的链接全部带 3000 端口,浏览器直接打开 404。
定位:v2.21.1 的 URL 体系分三层——NEXT_PUBLIC_WEBAPP_URL(对外、进邮件链接)、NEXT_PRIVATE_INTERNAL_WEBAPP_URL(服务端自调用)、PORT(容器内监听端口)。映射端口只改第一层即可,内部两层保持在容器视角。
修复:NEXT_PUBLIC_WEBAPP_URL=http://localhost:8130 与端口映射 '8130:3000' 同步。判断口诀:邮件里的链接对不对看 PUBLIC,服务间调用通不通看 INTERNAL,容器内监听看 PORT。
坑三:SMTP 未配,签署邀请发不出
现象:分发文档后收件人收不到签署请求,docker compose logs 可见邮件发送报错;更隐蔽的是注册收不到验证邮件。
定位:官方 compose 基线里 SMTP 指向 127.0.0.1,容器内并无邮件服务。v2.21.1 的邮件通道有 smtp-auth(默认)、smtp-api、resend、mailchannels 四种,NEXT_PRIVATE_SMTP_HOST/PORT 不通时整条签署链路断链。
修复:联调阶段挂一个 Mailpit(本实测编排已内置),生产环境换成真实 SMTP。判断邮件链路是否通,看 Mailpit 收件箱(http://宿主机:8133)有没有新增验证邮件最快。
五、备份与恢复
5.1 卷盘点
NEXT_PUBLIC_UPLOAD_TRANSPORT=database 时,整个栈只有一个业务数据卷:
docker volume ls --format '{{.Name}}' | grep documenso
docker volume inspect documenso-v2.21.1-stack_database --format '{{.Mountpoint}}'
实测回显:
documenso-v2211-stack_database
database 卷对应 PostgreSQL 数据目录,包含用户、文档元数据、PDF 二进制(文档表)与审计日志——即全部业务数据。certs/ 目录里的 .p12 与三把密钥是「数据之外的另一半」,必须与数据库备份同批管理。
5.2 数据库备份
推荐 PostgreSQL 自定义格式(-Fc,压缩且支持选择性恢复):
docker exec documenso-v2.21.1-db pg_dump -U documenso -d documenso --no-owner -Fc -f /tmp/documenso-backup.dump
docker cp documenso-v2.21.1-db:/tmp/documenso-backup.dump ./documenso-backup.dump
实测回显(备份文件 294 KB,覆盖 54 张表):
-rw-r--r-- 1 root root 294706 Oct 28 14:39 documenso-backup.dump
恢复时把 dump 拷回新库容器再 pg_restore:
docker cp ./documenso-backup.dump documenso-v2.21.1-db:/tmp/restore.dump
docker exec documenso-v2.21.1-db pg_restore -U documenso -d documenso --clean --if-exists /tmp/restore.dump
演练恢复后务必重启应用容器并复查 /api/health。备份策略与恢复演练的完整流程(含纯 SQL 导出、卷快照的取舍)在《Documenso 常用命令与运维实战》中有逐条命令实测。
六、版本升级
v2.21.1 的升级路径是「改镜像版本 → 重新 up → 迁移自动跑」:
- 修改
compose.yaml中documenso/documenso:v2.21.1为目标版本(如v2.22.0); docker compose pull documenso && docker compose up -d;- 观察启动日志的迁移段:新版本新增的迁移会自动应用,日志末尾出现
All migrations have been successfully applied.; - 复查
/api/health与/api/certificate-status双 ok。
两点纪律:升级前先做 5.2 节的数据库备份(迁移不可自动回退,跨大版本尤其如此);版本号固定写死在编排文件里,配合编排名 documenso-v2.21.1-stack 的命名规则,哪一版在跑一目了然。
相关文档
- 《Documenso 介绍与选型指南》:产品定位、功能边界与法律效力注记
- 《Documenso 配置参数详解》:全部环境变量分组逐项说明
- 《Documenso 常用命令与运维实战》:REST API 全流程实测与备份恢复演练