Gotenberg Docker Compose 部署实战
Gotenberg v8.39.0 Docker Compose 从零部署实战:镜像三变体选型、compose.yaml 编排与健康检查、/health 探活回显、字体目录挂载中文渲染、Basic 认证实测、数据持久化说明与运维命令。
Gotenberg Docker Compose 部署实战
目标:本文以官方镜像为唯一载体,在 Ubuntu 22.04 服务器上从零完成 Gotenberg v8.39.0 的 Docker Compose 单机部署,覆盖镜像变体选型、compose.yaml 编排、健康检查探活、数据持久化说明、字体目录挂载、Basic 认证开启与运维命令,全部步骤均已实测通过;配置参数的逐项详解与 API 调用实操见 《Gotenberg 配置参数详解》 与 《Gotenberg 常用命令与运维实战(核心篇)》。
一、环境信息
本章列出本次部署实测的服务器环境与版本组合,读者在相同或相近环境下可完全复现。
- 操作系统:Ubuntu 22.04(x86_64,内核 5.15+ 均可)
- Docker Engine:29.x(含 Docker Compose v2 插件,
docker compose子命令可用) - Gotenberg 版本:v8.39.0(官方主标签
gotenberg/gotenberg:8,2026-10-28 实查时8标签即指向 8.39.0) - API 端口规划:宿主机 8130 → 服务 3000(Gotenberg 默认监听 3000 端口)
- 编排网络:自定义 bridge 网络
gotenberg-net,显式网段 10.89.57.0/24 - Compose 项目名:
gotenberg-8-stack(在 compose.yaml 中显式声明,避免多项目同名互相干扰) - 配置目录:
/opt/gotenberg/(compose.yaml 与字体目录均放此处)
⚠️ 注意:Gotenberg 官方明确建议不要把服务直接暴露到公网(来源:官方安装文档),生产环境应部署在内网,仅对内网应用开放端口。
二、镜像选型说明
本节说明官方镜像的三种变体差异与标签选择策略,这是部署前最重要的决策点之一。
Gotenberg 官方提供三种镜像变体(来源:官方安装文档,2026-10-28 查询):
| 镜像标签 | 体积 | 包含能力 | 适用场景 |
|---|---|---|---|
gotenberg/gotenberg:8 | 完整体积(约 2.5GB 磁盘占用,实测) | Chromium + LibreOffice + 全部 PDF 引擎 | 网页/HTML/Markdown/Office 全功能转换 |
gotenberg/gotenberg:8-chromium | 约小 30% | Chromium + PDF 引擎,无 Office 转换 | 只转网页/HTML,不需要 Office |
gotenberg/gotenberg:8-libreoffice | 约小 40% | LibreOffice + PDF 引擎,无 URL/HTML/Markdown 转换与截图 | 只转 Office 文档与 PDF 后处理 |
三种变体均包含 PDF 引擎全套能力(合并、拆分、加密、水印、元数据、书签、附件、PDF/A 等)。请求了变体不支持的特性会直接返回错误。
标签策略:gotenberg/gotenberg:8 是跟随 8.x 最新稳定版的滚动标签,本次部署实测拉取到的就是 8.39.0。若要锁定版本,可用精确标签 gotenberg/gotenberg:8.39.0。官方另有 Cloud Run 与 AWS Lambda 专用变体(8-cloudrun、8-aws-lambda 后缀),仅对应云平台托管场景需要。
本次部署选择 full 变体 gotenberg/gotenberg:8:实测场景需要同时覆盖 Chromium 渲染与 Office 转换两条线,功能矩阵最全,代价是体积最大。如果业务只有单一路径,选对应精简变体可节省约三至四成磁盘与内存开销。
💡 说明:镜像以非 root 用户
gotenberg(UID/GID 1001)运行,自 8.21.0 起还支持 OpenShift 的任意 UID 模式(来源:官方安装文档)。无需在 compose.yaml 里额外配置user:,开箱即是降权运行。
三、前置条件
部署前需确认服务器满足以下条件,逐项核对可避免绝大多数启动失败。
- Docker 与 Compose 插件就绪:
# 检查 Docker 版本与 Compose 插件是否可用
docker --version && docker compose version
实测回显:
Docker version 29.8.1, build 61a5167
Docker Compose version v2.39.4
- 磁盘余量充足:full 镜像解压后磁盘占用约 2.5GB,建议预留 5GB 以上。
# 确认磁盘余量
df -h / | tail -1
实测回显:
/dev/sda2 443G 147G 278G 35% /
- 目标端口未被占用:本文用宿主机 8130 端口,确认其空闲。
# 检查端口占用(无输出即空闲)
ss -ltn | grep 8130
实测回显(空闲状态,grep 无命中后以退出码确认):
(无输出,端口空闲)
- 镜像拉取网络可达:官方镜像发布在 Docker Hub。国内网络拉取缓慢时可配置镜像加速器或代理;本次实测环境 Docker 守护进程已配置代理
192.168.1.62:7897,直接拉取成功。若拉取超时,在/etc/docker/daemon.json配置 registry-mirrors 或 HTTP 代理后重启 Docker 再试。
四、部署步骤
本章从建目录、写编排文件到启动、探活,给出完整的 0→1 部署主线,每一步都附实测回显。
4.1 创建部署目录
# 创建部署目录与字体挂载目录
mkdir -p /opt/gotenberg/fonts
cd /opt/gotenberg
目录用途:compose.yaml 放部署定义;fonts/ 用于向服务内挂载自定义字体(见第八章),本次实测放入了一份中文字体 DroidSansFallbackFull.ttf。
4.2 编写 compose.yaml
在 /opt/gotenberg/compose.yaml 写入以下内容(与本次实测文件一致):
name: gotenberg-8-stack
services:
gotenberg:
image: gotenberg/gotenberg:8
container_name: gotenberg-8-api
hostname: gotenberg.local
ports:
- "8130:3000"
networks:
gotenberg-net:
ipv4_address: 10.89.57.10
volumes:
- /opt/gotenberg/fonts:/usr/local/share/fonts/custom:ro
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
gotenberg-net:
driver: bridge
ipam:
config:
- subnet: 10.89.57.0/24
逐项说明:
name: gotenberg-8-stack:显式指定 Compose 项目名。多套服务共存于一台宿主机时,显式项目名可避免同名项目down时误删其他服务的资源;image: gotenberg/gotenberg:8:官方 full 变体,跟随 8.x 最新稳定版;ports: "8130:3000":宿主机 8130 映射到服务默认监听的 3000 端口,宿主机端口可按实际规划调整;networks+ipam:自定义 bridge 网络并固定网段与地址,便于同网络内其他服务按固定 IP 或容器名访问;网段需避开宿主机已有网段(10.89.57.0/24 为本次实测选用,无冲突即可);volumes:把宿主机字体目录只读挂载到/usr/local/share/fonts/custom(挂载点说明见第八章);restart: unless-stopped:异常退出自动拉起,人工停止后不自动重启;healthcheck:用curl --fail请求自身/health路由,30 秒一次、失败 3 次判为 unhealthy,启动后给 10 秒宽限期。
⚠️ 注意:Gotenberg 的配置不写在 compose.yaml 的自定义配置文件里,而是通过
environment环境变量或覆盖启动command传参(见第七章)。切勿试图挂载一个「gotenberg.conf」——它没有配置文件。
4.3 拉取镜像并启动
# 在部署目录内拉取镜像并后台启动
docker compose up -d
实测回显:
[+] Running 2/2
✔ Network gotenberg-8-stack_gotenberg-net Created
✔ Container gotenberg-8-api Started
首次执行会先拉取镜像(full 变体约 2.5GB,视带宽需数分钟);回显中网络与实例一次性创建即说明编排无语法问题。
4.4 确认运行状态与健康检查
# 查看编排状态,等待 healthcheck 通过
docker compose ps
实测回显(启动约 20 秒后):
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
gotenberg-8-api gotenberg/gotenberg:8 "gotenberg" gotenberg 28 seconds ago Up 26 seconds (healthy) 0.0.0.0:8130->3000/tcp, [::]:8130->3000/tcp
STATUS 列出现 (healthy) 即健康检查已通过。
# 查看启动日志确认模块加载
docker compose logs gotenberg | head -12
实测回显:
_____ __ __
/ ___/__ / /____ ___ / / ___ _______ _
/ (_ / _ \/ __/ -_) _ \/ _ \/ -_) __/ _ '/
\___/\___/\__/\__/_//_/_.__/\__/_/ \_, /
/___/
A Docker-based API for converting documents to PDF.
Version: 8.39.0
-------------------------------------------------------
[SYSTEM] modules: api chromium exiftool libreoffice libreoffice-api libreoffice-pdfengine pdfcpu pdfengines pdftk prometheus qpdf webhook
[SYSTEM] chromium: Chromium ready to start
日志确认三点:版本 8.39.0、全部模块(api/chromium/exiftool/libreoffice/pdfcpu/pdfengines/pdftk/prometheus/qpdf/webhook)已加载、Chromium 就绪待启动(默认首次转换时才拉起)。
4.5 API 探活验证
# 健康检查路由探活
curl -s http://localhost:8130/health
实测回显:
{"status":"up","details":{"chromium":{"status":"up","timestamp":"2026-10-28T05:08:50.01821323Z"},"libreoffice":{"status":"up","timestamp":"2026-10-28T05:08:50.018209691Z"}}}
# 版本路由与根路由状态码
curl -s http://localhost:8130/version; echo
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8130/
实测回显:
8.39.0
200
三连探活全部符合预期:/health 返回整体 up 且 Chromium、LibreOffice 两个引擎均 up;/version 返回与镜像一致的版本号;根路由返回 200。
💡 说明:
/health详情里 Chromium/LibreOffice 状态为up表示引擎进程可正常拉起。若某引擎异常,对应条目会变为其他状态,此时应先查docker compose logs中的启动报错。
五、数据持久化说明
本节解释 Gotenberg 的无状态设计与持久化需求,避免读者在存储规划上走弯路。
Gotenberg 不需要数据卷做业务持久化:它是无状态转换服务,每个请求的输入文件与输出 PDF 都在请求生命周期内处理完毕,临时文件存放在服务内部临时目录并在请求结束后清理。没有数据库、没有索引、没有需要跨重启保留的状态——down 再 up 服务即恢复如初。
实际需要挂载宿主机目录的只有三类场景:
- 自定义字体(只读挂载,见第八章)——解决中文等语言字体缺失问题;
- TLS 证书(只读挂载)——启用 HTTPS 时把证书与私钥文件传入,配合
API_TLS_CERT_FILE/API_TLS_KEY_FILE使用; - 核心转储调试(可选)——LibreOffice 异常崩溃排查时官方建议开启 core dump 目录挂载,常规部署无需。
因此本文 compose.yaml 中唯一的业务性挂载就是字体目录。日志输出到标准输出,由 Docker 日志驱动统一管理(docker compose logs 查看),也不需要单独落盘。
💡 说明:正因为无状态,Gotenberg 的水平扩展非常简单——把 compose.yaml 里的服务在多台服务器各起一份,前面挂任意负载均衡即可,实例之间零协调。这也是它与「自带存储的有状态服务」在部署上最大的区别。
六、环境变量表
Gotenberg 的所有配置项都有「命令行参数」与「环境变量」两种等价写法,在 Compose 中首选环境变量。本节列出部署阶段最常用的环境变量子集(完整参数体系见 《Gotenberg 配置参数详解》)。
环境变量命名规则:命令行参数 --api-port 对应环境变量 API_PORT——把参数名的连字符去掉、转大写即可。仅 Basic 认证的用户名密码两个变量带 GOTENBERG_ 前缀。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
API_PORT | 3000 | API 监听端口,改端口时需同步调整 ports 映射 |
API_BIND_IP | 0.0.0.0 | 监听绑定的 IP 地址 |
API_TIMEOUT | 30s | 单请求处理时限,超时返回 503 |
API_START_TIMEOUT | 30s | 服务自身启动时限 |
API_BODY_LIMIT | 无限制 | multipart 请求体上限,如 50MB |
API_ENABLE_BASIC_AUTH | false | 开启 Basic 认证开关 |
GOTENBERG_API_BASIC_AUTH_USERNAME | 无 | Basic 认证用户名(认证开启时必填) |
GOTENBERG_API_BASIC_AUTH_PASSWORD | 无 | Basic 认证密码(认证开启时必填) |
API_ENABLE_DEBUG_ROUTE | false | 开启 /debug 运行时配置导出路由 |
LOG_LEVEL | info | 日志级别(debug/info/warn/error) |
CHROMIUM_AUTO_START | false | 服务启动即预热 Chromium(默认首次转换时启动) |
LIBREOFFICE_AUTO_START | false | 服务启动即预热 LibreOffice |
WEBHOOK_ENABLE | — | Webhook 特性默认启用,置 WEBHOOK_DISABLE=true 可整体关闭 |
表格解读:常规内网部署通常只需要动两处——API_TIMEOUT(业务转换耗时大于 30 秒时调大)与认证三件套(API_ENABLE_BASIC_AUTH + 用户名 + 密码)。其余项保持默认即可。
七、配置参数说明
本节演示「改配置 → 重启 → 验证生效」的完整闭环。以两个最常用的配置项为例:请求体上限与 Basic 认证。
示例一:开启 Basic 认证(实测)
在 compose.yaml 的 gotenberg 服务下加入 environment 段:
environment:
API_ENABLE_BASIC_AUTH: "true"
GOTENBERG_API_BASIC_AUTH_USERNAME: admin
GOTENBERG_API_BASIC_AUTH_PASSWORD: q1w2e3r4
# 应用配置并滚动重建
docker compose up -d
实测回显:
[+] Running 1/1
✔ Container gotenberg-8-api Started
验证认证行为(实测回显):
/health 未带凭据: HTTP 200 # 健康检查路由豁免认证
转换路由未带凭据: HTTP 401 # 业务路由要求认证
转换路由带凭据(-u admin:q1w2e3r4): HTTP 200
/version 未带凭据: HTTP 401 # 版本路由同样受保护
/prometheus/metrics 未带凭据: HTTP 401
不存在路由: HTTP 404
结论:认证开关对 /version、/prometheus/metrics 与全部转换路由生效,唯独 /health 保持豁免——这是刻意的「liveness 探针友好」设计,负载均衡与编排平台的健康探测无需配置凭据。
示例二:调大请求体上限与超时(配置写法)
environment:
API_BODY_LIMIT: "50MB"
API_TIMEOUT: "60s"
API_BODY_LIMIT 从无限制收紧为 50MB 可防止异常大包打满内存;API_TIMEOUT 调大到 60 秒可容纳慢转换(长网页渲染、大文档导出)。两个变量同样走 docker compose up -d 应用。
⚠️ 注意:环境变量值统一按字符串书写(如
"true"、"50MB")。配置错误时服务可能启动失败,用docker compose logs gotenberg查看具体报错项即可定位。
八、配置文件挂载
Gotenberg 没有配置文件,本节说的「文件挂载」是指把字体等资源文件传入服务——这是中文环境部署的关键一环。
问题背景:PDF 渲染依赖字体。服务内预装了 Noto 系列 CJK 字体(实测环境 fc-list 统计共 345 款字体,其中 Noto/文泉驿等 CJK 相关字体 302 款),中文 HTML 与 Office 文档默认即可正常渲染。但遇到小语种、商用授权字体或设计稿指定字体时,就需要把字体文件挂载进去。
挂载方法(本文 compose.yaml 已包含):
volumes:
- /opt/gotenberg/fonts:/usr/local/share/fonts/custom:ro
把 .ttf/.otf 字体文件放入宿主机 /opt/gotenberg/fonts/ 后重启服务。验证字体是否被识别:
# 在服务内执行 fc-list 过滤新挂载的字体
docker exec gotenberg-8-api fc-list | grep -i droid
实测回显(挂载 DroidSansFallbackFull.ttf 后):
/usr/local/share/fonts/custom/DroidSansFallbackFull.ttf: Droid Sans Fallback:style=Regular
字体被 fontconfig 识别后,Chromium 与 LibreOffice 转换时即可按字体名引用。移除挂载并重启,字体随之消失——方便做版本化字体管理。
💡 说明:挂载点选
/usr/local/share/fonts/custom而非覆盖系统字体目录,是只读追加式设计:不破坏内置 Noto 字体集,出问题移除挂载即可回滚。若要「彻底替换默认字体集」,才考虑覆盖/usr/local/share/fonts整个目录,但需保证目录内字体完整可用。
九、等价 docker run
不使用 Compose 的读者,可以用单条 docker run 命令实现与本文 compose.yaml 等价的部署。
# 与本文 compose.yaml 等价的最小 docker run
docker run -d \
--name gotenberg-8-api \
-p "8130:3000" \
--restart unless-stopped \
gotenberg/gotenberg:8
带字体挂载与自定义网络的形式:
# 创建自定义网络(网段按需调整,避开宿主机已有网段)
docker network create --subnet 10.89.57.0/24 gotenberg-net
# 启动:挂载字体目录 + 接入自定义网络
docker run -d \
--name gotenberg-8-api \
--network gotenberg-net \
-p "8130:3000" \
-v /opt/gotenberg/fonts:/usr/local/share/fonts/custom:ro \
--restart unless-stopped \
gotenberg/gotenberg:8
官方文档的最简形式则更短:docker run --rm -p "3000:3000" gotenberg/gotenberg:8——--rm 表示停止后自动清理,适合一次性试用;若要更安全可绑定本机回环 127.0.0.1:3000:3000,仅允许本机访问。
两种方式差异对照:
| 维度 | Compose | docker run |
|---|---|---|
| 声明式配置与版本管理 | ✅ compose.yaml 可入库 | ❌ 命令行参数散落 |
| 健康检查内建 | ✅ healthcheck 段 | ❌ 需 --health-* 系列参数 |
| 多服务编排 | ✅ 原生支持 | ❌ 单服务 |
| 一键停止清理 | docker compose down | docker rm -f 手工 |
单服务部署两者皆可;计划纳入基础设施代码管理的生产环境建议 Compose。
十、常用运维命令
本节汇总日常运维高频命令,全部为实测可用形态。
# 查看运行状态(含健康状态)
docker compose ps
# 查看最近 100 行日志
docker compose logs --tail 100 gotenberg
# 重启服务(配置变更后也可用 up -d 滚动重建)
docker compose restart gotenberg
# 升级到 8.x 最新版(滚动标签场景)
docker compose pull && docker compose up -d
# 临时拉起命令行做字体等运行时检查
docker exec -it gotenberg-8-api sh
# 停止并清理编排(网络随项目一并清理)
docker compose down
关键命令的实测说明:
docker compose pull && docker compose up -d是滚动升级的标准动作:pull 拉新版,up -d 只重建镜像变化的实例,数据无状态不受影响;- 升级后用
curl -s http://localhost:8130/version核对版本号是否已变; docker compose down会删除容器与自定义网络;字体目录与 compose.yaml 在宿主机上不受影响,重新up -d即完整恢复。
监控接入:Gotenberg 内置 Prometheus 指标端点,路径 /prometheus/metrics(默认路径,可用 PROMETHEUS_METRICS_PATH 改)。典型指标如 gotenberg_chromium_requests_queue_size(Chromium 排队长度)、gotenberg_chromium_restarts_count(Chromium 重启次数)等,可直接被抓取,用于容量观察与告警。
十一、常见问题
本章沉淀部署阶段的高频问题与排障路径,均来自实测或官方文档明确记载的行为。
Q1:启动后 /health 一直 503 或连接被拒?
先看日志:docker compose logs gotenberg。常见两类原因:一是宿主机端口冲突,ports 映射的宿主端口已被占用导致启动失败,换端口即可;二是启动时限太紧,磁盘或 CPU 紧张时初始化超过 API_START_TIMEOUT(默认 30s),可适当调大。健康检查自身也受 healthcheck.start_period 影响,刚启动 10 秒内 unhealthy 属正常。
Q2:中文渲染成方块(豆腐块)怎么办?
根因是字体缺失。先确认内置字体:docker exec gotenberg-8-api fc-list | grep -ci cjk,正常应有数百款 CJK 字体(实测 302 款)。若确需自定义字体,按第八章挂载字体目录并重启,再用 fc-list | grep 字体名 确认识别。字体被识别后重新转换即可。
Q3:开启了 Basic 认证后监控抓取失败?
/prometheus/metrics 受认证保护(实测 401)。两种解法:监控端抓取时携带凭据;或仅对监控来源放宽——由于认证是全服开关,实践中更常见的做法是保留认证并让 Prometheus 配置 basic_auth 凭据。
Q4:镜像拉取超时?
官方镜像在 Docker Hub。按「官方源 → 国内加速器/代理」的顺序处理:先直接 pull;超时则配置镜像加速器(/etc/docker/daemon.json 的 registry-mirrors)或 HTTP 代理后重启 Docker 再拉。本次实测环境走代理 192.168.1.62:7897 拉取成功,拉取大小约 1.5GB(压缩态)。
Q5:如何限制单个转换请求的资源占用?
服务本身不提供单请求级资源配额。工程上的通用做法:入口用 API_BODY_LIMIT 限制请求体大小;编排层限制实例级 CPU/内存;再大的文件与耗时通过 Webhook 异步化削峰。三者组合即可稳定生产行为。
十二、参考链接
- 官方安装文档(镜像变体/部署形态):https://gotenberg.dev/docs/getting-started/installation
- 官方配置文档(参数与环境变量):https://gotenberg.dev/docs/configuration
- 健康检查路由文档:https://gotenberg.dev/docs/system/get-health-check
- 本专栏姊妹篇——《Gotenberg 介绍与选型指南》
- 本专栏姊妹篇——《Gotenberg 配置参数详解》
- 本专栏姊妹篇——《Gotenberg 常用命令与运维实战(核心篇)》