Documenso Docker Compose 部署实战

Documenso v2.21.1 社区版生产编排实测:自签签署证书、官方 Compose 双服务基线(应用 + PostgreSQL + Mailpit)、首启迁移日志走读、管理员账号初始化,沉淀证书挂载权限、URL 体系、SMTP 三类真实排障与数据库备份恢复。

Documenso电子签名PDF 处理Nodejs2026/10/28

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-stackcompose.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 → 迁移自动跑」:

  1. 修改 compose.yaml 中 documenso/documenso:v2.21.1 为目标版本(如 v2.22.0);
  2. docker compose pull documenso && docker compose up -d;
  3. 观察启动日志的迁移段:新版本新增的迁移会自动应用,日志末尾出现 All migrations have been successfully applied.;
  4. 复查 /api/health 与 /api/certificate-status 双 ok。

两点纪律:升级前先做 5.2 节的数据库备份(迁移不可自动回退,跨大版本尤其如此);版本号固定写死在编排文件里,配合编排名 documenso-v2.21.1-stack 的命名规则,哪一版在跑一目了然。

相关文档