Signature PDF 1.11.0 Docker Compose 部署指南
在 Ubuntu 26.04 服务器上用 Docker Compose 从零部署 Signature PDF 1.11.0 官方镜像,完成端口规划、上传限额、共享存储持久化与协议层功能验证,含镜像拉取降级实录与等价 docker run。
Signature PDF 1.11.0 Docker Compose 部署指南
目标:在一台 Ubuntu 服务器上用 Docker Compose 从零部署 Signature PDF 1.11.0(24eme/signaturepdf 官方镜像 xgaia/signaturepdf),完成端口规划、上传限额、共享存储持久化与协议层功能验证。已在 Ubuntu 26.04.1 LTS(x86_64,Docker 29.9.2 / Compose v5.6.1)实测通过。
一、环境信息
- 操作系统:Ubuntu 26.04.1 LTS(x86_64,内核 7.0.0-38-generic)
- Docker:Docker 29.9.2、Docker Compose v5.6.1(v2.x 语法兼容)
- 应用版本:Signature PDF v1.11.0(官方镜像
xgaia/signaturepdf:1.11.0,镜像 IDe4e417983384,磁盘占用 1.37GB / 内容 344MB) - 应用栈:Apache 2.4.69(Debian)+ PHP 8.2.35,基础镜像
php:8.2-apache(来源:镜像 DockerfileFROM php:8.2-apache,启动日志回显Apache/2.4.69 (Debian) PHP/8.2.35 configured -- resuming normal operations) - 应用监听端口:
8140(映射到应用内部 Apache 80 端口) - 数据目录:
./data(bind mount 到应用内/data,存放多人协作共享的 PDF) - 内网网段:
10.89.60.0/24(独立 Compose 网络,网关10.89.60.1,应用固定地址10.89.60.10) - 上传限额:单文件 48M、POST 48M、单次最多 401 个文件(即 400 页 + 原 PDF)
💡 技术拓展:Signature PDF 官方 installation.md 提供四条安装路线,形态对比——
路线 形态 特点 Docker(官方镜像 xgaia/signaturepdf) 单服务、零数据库 本文采用;依赖全部打进镜像,升级换 tag 即可 Debian/Ubuntu 裸机 git clone + php -S或 Apache需自装 php/librsvg2-bin/pdftk/imagemagick/potrace/ghostscript/gpg 六件套 deb 包( make deb)系统包托管 依赖由 apt 解析,适合纯 Debian 系批量装机 Alpine 脚本 / YunoHost 社区安装脚本 非官方镜像形态,组件版本自行把控 本文选 Docker 路线:该应用无数据库、无队列,一个服务即可跑通全部功能,是容器化成本最低的一类 Web 应用。裸机与 deb 路线的依赖清单与 PHP/Apache 配置见第九章等价 docker run 对照及官方 installation.md。
二、镜像选型说明
Signature PDF 仓库本身不含官方 Compose 模板,官方推荐的容器入口是 installation.md 中的一条命令:
docker run -d --name=signaturepdf -p 8080:80 xgaia/signaturepdf
xgaia/signaturepdf 是项目维护方在 Docker Hub 发布的官方镜像(README 徽章直链),版本 tag 与 GitHub tag 同步(1.11.0、1.10.0 等)。本文固定使用 1.11.0 而非 latest,保证升级可控。镜像内配置机制(《Signature PDF 1.11.0 配置参数详解》 有逐项实测):启动时 entrypoint.sh 用 envsubst 把三个模板(apache.conf、php.ini、config.ini.tpl)渲染为生效配置,因此所有部署参数都通过环境变量注入,无需挂载配置文件。
⚠️ 网络情况说明(2026-11-02 实测):本机拉取官方镜像时按「官方源 → 国内源 → 代理」降级链执行,过程如下——
docker pull xgaia/signaturepdf:latest(Docker Hub 官方源)——长时间挂起,超过 40 分钟无进展,终止;docker pull docker.m.daocloud.io/xgaia/signaturepdf——DaoCloud 加速器返回「这镜像不在白名单」(public-image-mirror 白名单机制未收录该仓库),失败;docker pull docker.1ms.run/xgaia/signaturepdf:1.11.0——成功,实测回显:
1.11.0: Pulling from xgaia/signaturepdf
Digest: sha256:e4e4179833848bd874b99a55720718a08bf857552a30623e734c92af41aeded2
Status: Downloaded newer image for docker.1ms.run/xgaia/signaturepdf:1.11.0
拉取后将 tag 规范回官方名(docker tag docker.1ms.run/xgaia/signaturepdf:1.11.0 xgaia/signaturepdf:1.11.0),digest 与 Docker Hub 官方一致(e4e417983384)。另备有 docker save 产物 xgaia-signaturepdf-1.11.0.tar(343,920,640 字节),离线环境可 docker load -i 直接导入。本机代理实况:192.168.1.72:7897 可用(curl -x 测试 registry-1.docker.io 返回 401=连通),192.168.1.62:7897 已失效(连接返回 000);若你的环境可直连 Docker Hub,第 1 步即可成功,无需降级。
三、前置条件
部署前确认服务器满足以下条件,逐项给出验证命令与实测回显:
- Docker 与 Compose 插件可用:
docker --version && docker compose version
实测回显:
Docker version 29.9.2, build a82e528
Docker Compose version v5.6.1
- 目标端口未被占用。本文选用 8140 端口,确认方式:
ss -ltn | grep 8140 || echo "port 8140 free"
实测回显(空闲时):
port 8140 free
- 规划 Compose 专用网段。为避免与服务器上已有网络冲突,先查看现有网段占用:
docker network ls -q | xargs docker network inspect --format '{{.Name}} -> {{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null | grep '10\.89\.'
实测回显(节选,按你服务器实际输出为准):
papermerge-353-stack_papermerge-net -> 10.89.57.0/24
plainpad-129-net -> 10.89.58.0/24
marmot-020-stack_marmot-net -> 10.89.56.0/24
⚠️ 注意:网段选择必须避开已有网络(包括已停止服务遗留的网络),冲突时创建会报
invalid pool request: Pool overlaps with other one on this address space。本文实测 10.89.60.0/24 空闲,选用之。创建前务必以上方命令的全量输出为准。
- 创建部署目录与共享存储目录:
mkdir -p /opt/signaturepdf/data
无输出即成功,用 ls -d /opt/signaturepdf/* 确认目录已创建。data 目录属主无需手工调整——应用启动时入口脚本会自动 mkdir -p 并 chown www-data(见第五章)。
四、部署步骤
4.1 编写 Compose 文件
在 /opt/signaturepdf/ 下创建 docker-compose.yml,内容如下(与本文实测文件一致):
name: signaturepdf-1.11.0-stack
services:
signaturepdf:
image: xgaia/signaturepdf:1.11.0
container_name: signaturepdf-1.11.0-web
restart: unless-stopped
ports:
- "8140:80"
environment:
SERVERNAME: "localhost:8140"
UPLOAD_MAX_FILESIZE: "48M"
POST_MAX_SIZE: "48M"
MAX_FILE_UPLOADS: "401"
PDF_STORAGE_PATH: "/data"
DEFAULT_LANGUAGE: "en_US.UTF-8"
volumes:
- ./data:/data
networks:
spnet:
ipv4_address: 10.89.60.10
networks:
spnet:
driver: bridge
ipam:
config:
- subnet: 10.89.60.0/24
gateway: 10.89.60.1
逐项说明:
name: signaturepdf-1.11.0-stack:显式指定 Compose 项目名。注意 Compose 会将项目名中的点号规范化为短横线,实际生成的网络名为signaturepdf-1110-stack_spnet,后续跨目录清理需用规范化后的名称(见第十章)。image: xgaia/signaturepdf:1.11.0:固定版本号而非latest,与 GitHub v1.11.0 tag 对应。container_name: signaturepdf-1.11.0-web:固定命名便于docker logs/docker exec定位;单副本部署专用,多副本时删掉此行。"8140:80":应用内部为 Apache 固定监听的 80 端口,宿主侧映射为 8140。SERVERNAME: "localhost:8140":渲染进 Apache 虚拟主机ServerName。生产环境填实际域名(如pdf.example.org),否则 Apache 启动日志会出现AH00558: Could not reliably determine the server's fully qualified domain name告警(本文实测日志原样出现该行,功能不受影响)。UPLOAD_MAX_FILESIZE/POST_MAX_SIZE/MAX_FILE_UPLOADS:三个 PHP 限额,官方默认 24M/24M/201,本文放宽到 48M/48M/401(详见第六章)。PDF_STORAGE_PATH: "/data":多人协作共享的 PDF 存储目录;留空则整个共享模式关闭(签名页隐藏「分享」入口)。DEFAULT_LANGUAGE: "en_US.UTF-8":官方默认fr_FR.UTF-8;该值决定启动时locale-gen生成哪个系统 locale,进而决定界面默认语言(见第十一章 Q4)。./data:/data:共享存储 bind mount 到宿主目录,卸载重建不丢数据(第五章)。ipv4_address: 10.89.60.10:固定内网地址,便于前置反代与健康检查写死目标。
4.2 启动服务栈
cd /opt/signaturepdf && docker compose up -d
实测回显:
Network signaturepdf-1110-stack_spnet Creating
Network signaturepdf-1110-stack_spnet Created
Container signaturepdf-1.11.0-web Creating
Container signaturepdf-1.11.0-web Created
Container signaturepdf-1.11.0-web Starting
Container signaturepdf-1.11.0-web Started
确认运行状态:
docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Ports}}'
实测回显:
NAME STATUS PORTS
signaturepdf-1.11.0-web Up 5 seconds 0.0.0.0:8140->80/tcp, [::]:8140->80/tcp
4.3 验证 Web 服务就绪
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8140/signature
实测回显:
200
首页(四大功能 Tab)同样返回 200,响应头可见服务端指纹:
HTTP/1.1 200 OK
Server: Apache/2.4.69 (Debian)
X-Powered-By: Fat-Free Framework
Set-Cookie: PHPSESSID=81f9177d9e31336ac5d109828bc74545; path=/; HttpOnly; SameSite=Lax
首次启动的日志可确认入口脚本完成了 locale 生成与 Apache 拉起:
docker compose logs --tail 5
实测回显(节选):
Generating locales (this might take a while)...
en_US.UTF-8... done
Generation complete.
[Mon Nov 02 05:02:58.187798 2026] [mpm_prefork:notice] [pid 18:tid 18] AH00163: Apache/2.4.69 (Debian) PHP/8.2.35 configured -- resuming normal operations
💡 说明:Signature PDF 无数据库、无初始化步骤,
up完成即可用。浏览器打开http://<服务器IP>:8140/,上传一份 PDF、画一个签名、点下载即完成最小闭环验证;多人协作共享与 API 的协议层实测见第十一章与 《Signature PDF 1.11.0 API 使用详解》。
五、数据持久化说明
本栈唯一的持久化数据是多人协作共享目录,通过 bind mount 落到宿主机:
| 数据 | 应用内路径 | 宿主机路径 | 内容 |
|---|---|---|---|
| 共享 PDF | /data | /opt/signaturepdf/data | 每个共享一个 32/20 位哈希目录 + 两个旁路文件 |
| 配置 | /usr/local/signaturepdf/config/config.ini | 未挂载 | 启动时由环境变量渲染生成,重建即复原(见第八章) |
实测共享目录内部结构(一次「发起共享 + 追加签名」后):
data/
├── fjnvuogj6a8s89kpq46p/ # 共享目录,目录名即分享链接中的 hash
│ ├── original.pdf # 发起人上传的原始 PDF(1061 字节)
│ ├── filename.txt # 原始文件名(供下载时还原命名)
│ ├── 20261102045840137443.svg.pdf # 第 1 层签名(SVG 转成的 PDF 图层)
│ ├── 20261102045840137443.sign.pdf # 第 1 层压印后的累积结果
│ ├── 20261102045857545781.svg.pdf # 第 2 层签名
│ ├── 20261102045857545781.sign.pdf # 第 2 层压印后的累积结果
│ └── final.pdf # 编译产物:当前全部层合成结果
├── fjnvuogj6a8s89kpq46p.admin # 20 位管理密钥(删除共享用)
└── fjnvuogj6a8s89kpq46p.expire # 0 字节文件,mtime 即过期时刻
三点务必知晓:
- 单人签名不落盘:不点「分享」的签名走流式处理,处理完即回传、服务器不留文件(README 原文承诺),
/data中不会出现任何目录;只有共享模式才写盘。 .expire的 mtime 就是保留期限:发起共享选「+1 hour」,则stat该文件看到 mtime = 创建时刻 + 1 小时;每次追加签名都会按保留期重新续期(实测两人先后签署后 mtime 刷新为第二人签署时刻 + 1 小时)。过期清理由GET /cron触发(第十章给出定时任务写法)。- 加密共享时全部文件带
.gpg后缀:启用PDF_STORAGE_ENCRYPTION=true后,目录内所有文件为 AES256 对称加密(original.pdf.gpg、final.pdf.gpg等),密钥只存在于发起人浏览器 Cookie 与分享链接#片段中,服务器端不可读明文(实测见 《Signature PDF 1.11.0 配置参数详解》)。
备份只需打包宿主机 data/ 目录;恢复同理解包回原路径并 chown 给 Web 运行用户。
六、环境变量表
官方 installation.md「Deploy with Docker → Configuration」节给出的部署变量,与本文实测取值对照:
| 变量 | 作用 | 官方默认 | 本部署取值 |
|---|---|---|---|
SERVERNAME | Apache ServerName(部署 URL) | localhost | localhost:8140 |
UPLOAD_MAX_FILESIZE | 单个上传文件大小上限 | 24M | 48M |
POST_MAX_SIZE | 整个 POST 请求体上限 | 24M | 48M |
MAX_FILE_UPLOADS | 单次请求文件数上限 = 页数 + 1 | 201(200 页) | 401(400 页) |
PDF_STORAGE_PATH | 共享 PDF 存储目录;留空关闭共享模式 | /data | /data |
DISABLE_ORGANIZATION | 关闭页面整理 Tab | false | 未设置(false) |
PDF_DEMO_LINK | 演示 PDF 链接:true 显示 / false 隐藏 / 自定义 URL | true | 未设置(显示默认示例) |
DEFAULT_LANGUAGE | 界面默认语言(同时决定 locale-gen 目标) | fr_FR.UTF-8 | en_US.UTF-8 |
PDF_STORAGE_ENCRYPTION | 共享存储 GPG 对称加密开关 | false | 未设置(false) |
限额的实际生效值可从页面内嵌 JS 变量直接读到(无需进服务器查 php.ini):
curl -s http://localhost:8140/signature | grep -oE 'maxSize = [0-9]+|maxPage = [0-9]+'
实测回显:
maxSize = 48000000
maxPage = 400
解读:maxSize 取 POST_MAX_SIZE 与 UPLOAD_MAX_FILESIZE 的较小者(48M 按 1000 进制折算为 48000000 字节);maxPage = MAX_FILE_UPLOADS - 1。三个变量在签名页、整理页、压缩页同时生效,上传超限文件会被 PHP 在落盘前拒收。
💡 技术拓展:容量单位在 PHP 配置里按 1000 进制折算(
M=×1000×1000),与操作系统的 1024 进制 MiB 不同——48M 实际是 48,000,000 字节 ≈ 45.8 MiB。设限额时若按「48 MiB」预期,应填50M。本文实测48M→48000000印证了这一折算规则。
七、配置参数说明
环境变量注入后并非直接生效,中间有一层「模板渲染」链(v1.11.0 源码 entrypoint.sh 实查):
docker compose up
└─ entrypoint.sh
├─ envsubst < config/apache.conf > /etc/apache2/sites-available/signaturepdf.conf # ${SERVERNAME}
├─ envsubst < config/php.ini > /usr/local/etc/php/conf.d/uploads.ini # 三个限额变量
├─ envsubst < config/config.ini.tpl > /usr/local/signaturepdf/config/config.ini # 4 个应用键
├─ sed 取消注释 /etc/locale.gen 中的 $DEFAULT_LANGUAGE && locale-gen
├─ export LANG/LANGUAGE/LC_ALL=$DEFAULT_LANGUAGE
├─ mkdir -p $PDF_STORAGE_PATH && chown www-data:www-data
└─ apache2-foreground
其中 config.ini.tpl 只渲染四个键(PDF_STORAGE_PATH、DISABLE_ORGANIZATION、PDF_DEMO_LINK、PDF_STORAGE_ENCRYPTION),实测生成的 config.ini 开头:
[globals]
; Path to which stored pdf to activate the mode of sharing a signature to several.
PDF_STORAGE_PATH=/data
; Disable organization tab and routes
DISABLE_ORGANIZATION=false
; Manage demo link pdf : true (by default, show), false (hide), or custom link
PDF_DEMO_LINK=
; Encryption activation (default activation if GPG is installed)
PDF_STORAGE_ENCRYPTION=false
应用其余配置项(管理页授权 IP、本地文件 API、元数据默认字段、自定义保留期、NSS 数字签名证书等)不在模板里,需要直接改 config.ini——逐项含义与实测行为见 《Signature PDF 1.11.0 配置参数详解》。
八、配置文件挂载
不要把 config.ini 直接 bind mount 进去:第七章的渲染链每次启动都会 envsubst 覆写该文件,挂载的文件内容会在启动瞬间被模板+环境变量重写的结果覆盖。实测佐证:向 config.ini 追加自定义段后 docker restart,重启后文件恢复为模板渲染的四个键。
需要持久化 config.ini 级配置(如 ADMIN_AUTHORIZED_IP、[signature] retention)时,两种正确做法:
- 挂载替换模板(推荐,改动最小):把定制过的
config.ini.tpl挂到/usr/local/signaturepdf/config/config.ini.tpl,启动时 envsubst 渲染的就是你的模板; - 派生镜像:
FROM xgaia/signaturepdf:1.11.0+RUN sed -i '$a ...' /usr/local/signaturepdf/config/config.ini.tpl,把模板改动固化进镜像。
挂载示例(做法 1):
volumes:
- ./data:/data
- ./config.ini.tpl:/usr/local/signaturepdf/config/config.ini.tpl:ro
九、等价 docker run
不使用 Compose 的等价单机跑法(与本文第四章等价,参数一一对应):
docker network create --subnet 10.89.60.0/24 --gateway 10.89.60.1 signaturepdf-net
docker run -d --name signaturepdf-1.11.0-web \
-p 8140:80 \
-e SERVERNAME="localhost:8140" \
-e UPLOAD_MAX_FILESIZE=48M -e POST_MAX_SIZE=48M -e MAX_FILE_UPLOADS=401 \
-e PDF_STORAGE_PATH=/data \
-e DEFAULT_LANGUAGE=en_US.UTF-8 \
-v /opt/signaturepdf/data:/data \
--network signaturepdf-net --ip 10.89.60.10 \
--restart unless-stopped \
xgaia/signaturepdf:1.11.0
官方最小跑法(全默认值:24M 限额、200 页上限、法文界面、存储目录 /data):
docker run -d --name=signaturepdf -p 8080:80 xgaia/signaturepdf
裸机对照(Debian/Ubuntu,官方 installation.md 原文):apt-get install php librsvg2-bin pdftk imagemagick potrace ghostscript locales gpg 装齐依赖后 git clone 源码、php -S localhost:8000 -t public 即可起服务;PHP 侧手工设置 upload_max_filesize=24M、post_max_size=24M、max_file_uploads=201,Apache 侧配 FallbackResource /index.php。
十、常用运维命令
以下命令均在 /opt/signaturepdf/(compose 文件所在处)执行。
# 查看运行状态
docker compose ps
# 跟踪应用日志(Apache 前台日志,含 PHP 报错)
docker compose logs -f
# 重启应用(数据不动)
docker compose restart
# 停止并移除栈(bind mount 数据不受影响)
docker compose down
# 跨目录彻底清理时用规范化项目名(点号被转为短横线)
docker compose -p signaturepdf-1110-stack down
过期共享清理——应用没有内置定时器,/cron 路由需外部触发(实测 curl http://localhost:8140/cron 返回 200,过期目录被删除)。加一条系统 crontab 每小时清理:
(crontab -l 2>/dev/null; echo '0 * * * * curl -s http://localhost:8140/cron -o /dev/null') | crontab -
⚠️ 注意:实测发现
/cron只删除过期共享目录与.expire文件,不清理旁路的<hash>.admin密钥文件(源码MainController::cron无该逻辑),长期运行会残留零星 20 字节小文件,可定期find /opt/signaturepdf/data -maxdepth 1 -name '*.admin' ! -exec test -e {} \; -delete级别的脚本或人工巡检处理(对照同名目录已不存在的才删)。
数据备份与恢复:
# 备份共享存储(含 .admin/.expire 旁路文件,恢复后管理密钥仍有效)
tar czf /opt/signaturepdf/data_backup_$(date +%F).tar.gz -C /opt/signaturepdf data
# 恢复:解回原位并交给 Web 运行用户
tar xzf /opt/signaturepdf/data_backup_2026-11-02.tar.gz -C /opt/signaturepdf
docker compose exec signaturepdf chown -R www-data:www-data /data
升级到新版本:改 compose 中 image: xgaia/signaturepdf:<新版本> 后 docker compose up -d 自动重建;回滚同理改回旧 tag。该应用无数据库迁移,版本切换零成本,但共享目录格式跨版本兼容(v1.5 以来布局未变),升级前后无需转换数据。
十一、常见问题
Q1:docker pull xgaia/signaturepdf 挂起或报「不在白名单」?
按第二章降级链处理:官方 Docker Hub 当时不可用(长时间挂起),DaoCloud 加速器白名单未收录该仓库;实测 docker.1ms.run/xgaia/signaturepdf:1.11.0 可拉取,拉完 docker tag 改回官方名即可。离线环境用 docker load -i xgaia-signaturepdf-1.11.0.tar。
Q2:发起共享返回 500 Internal Server Error?
实测复现:对同一个 hash 值重复 POST /share(例如浏览器回退后重复提交同一分享表单)返回 500,错误详情 mkdir(): File exists——共享目录已存在时 createShare 直接 mkdir 失败。hash 由前端随机生成(20 位小写字母数字),正常操作不会相撞;用 API 自建 hash 撞了换一个即可。
Q3:上传大 PDF 报「文件过大」或页面静默失败?
三重限额共同约束:单文件 ≤ UPLOAD_MAX_FILESIZE、请求体 ≤ POST_MAX_SIZE、文件数 ≤ MAX_FILE_UPLOADS - 1(页数上限)。整理多份 PDF 时请求体是「所有文件之和」,最容易先撞 POST_MAX_SIZE。改 compose 环境变量后 docker compose up -d 重建生效,用 curl http://localhost:8140/signature | grep maxSize 复核实际值(第六章)。
Q4:界面是英文/法文,想要中文?
DEFAULT_LANGUAGE 只控制默认 locale(可选值受镜像内 locale 限制,官方镜像构建时只装 locales 包、启动时按该变量 locale-gen)。界面语言实际由浏览器 Accept-Language 与 ?lang=/LANGUAGE Cookie 决定:访问 http://localhost:8140/signature?lang=fr 实测返回法文界面(Signer un PDF),响应头带 Set-Cookie: LANGUAGE=fr。中文(zh)不在官方 15 种支持语言内,需自行到 hosted.weblate.org 参与翻译。
Q5:/administration 管理页打不开,返回 You (x.x.x.x) are not allowed to access this file?
管理页默认只放行 localhost、127.0.0.1、::1 三个来源 IP(源码 Config.class.php 硬编码合并进 ADMIN_AUTHORIZED_IP)。经宿主端口映射访问时来源 IP 是 Docker 网关地址(本文环境为 10.89.60.1),需在 config.ini 的 ADMIN_AUTHORIZED_IP 追加授权 IP(空格分隔)。配置方法与生效实测见 《Signature PDF 1.11.0 配置参数详解》。
Q6:多人签名的 PDF 什么时候从服务器消失?
两条路径:① 发起人持管理密钥删除(GET /signature/<hash>/delete/<adminkey>,实测 302 且目录、.admin、.expire 三者即刻清除);② 保留期到期后由 /cron 清理(第十章)。追加签名会续期。加密共享的 .gpg 文件即使被拖库也无法还原明文,但过期前始终占盘。
十二、参考链接
- 官方仓库:https://github.com/24eme/signaturepdf (installation.md Docker 节,2026-11-02 实查)
- 官方镜像:https://hub.docker.com/r/xgaia/signaturepdf
- 本专栏姊妹篇:《Signature PDF 1.11.0 介绍与选型指南》、《Signature PDF 1.11.0 配置参数详解》、《Signature PDF 1.11.0 API 使用详解》