Papermerge 3.5.3 配置参数详解

Papermerge 3.5.3 配置参数详解:PAPERMERGE__ 环境变量分层机制、四组件 config.py 默认值实测、数据库/检索/OCR/认证参数逐项与死配置白名单陷阱

PapermergePapermerge文档管理2026/11/02

Papermerge 3.5.3 配置参数详解

目标:把 Papermerge 3.5.3 的全部配置参数讲透——配置的分层机制、每个组件的配置文件在哪、每个参数的代码默认值与生产改法,以及实测踩到的"死配置""假白名单"陷阱。

本文以《Papermerge 3.5.3 Docker Compose 部署指南》部署的六服务栈(webapp + db + redis + solr + ocr_worker + i3worker)为实测环境;组件定位与选型见《Papermerge 介绍与选型指南》;REST API 用法见本专栏《Papermerge 3.5.3 API 使用详解》。文中所有命令与回显均为 2026-11-02 在该栈上实测照抄,示例账号 admin / q1w2e3r4。

一、环境信息

项目值
操作系统Linux(x86_64,内核 7.0.0-38-generic)
应用版本Papermerge 3.5.3(papermerge/papermerge:3.5.3)
配置机制环境变量 PAPERMERGE__<SECTION>__<KEY>(pydantic-settings),无用户可编辑的 settings.py
配置载体每个组件一个 Settings 类:webapp / auth-server / ocrworker / i3worker 各自独立
实测环境《Papermerge 3.5.3 Docker Compose 部署指南》六服务栈,Web 入口 :8138
示例凭据admin / q1w2e3r4(首启由 PAPERMERGE__AUTH__* 自动创建)

3.x 与 2.x 的配置体系完全不同:2.x 是 Django 时代的 settings.py,3.x 重写后没有任何用户可直接编辑的配置文件,全部配置经由环境变量注入各组件的 pydantic-settings Settings 类,代码默认值内置在镜像内的 config.py 里。本文的"参数详解"因此以镜像内 config.py 原文 + 实测回显为准,官方文档(docs.papermerge.io settings/*,页脚 June 1, 2026,查询日期 2026-11-01)作为对照。

二、前置条件

  1. 已按《Papermerge 3.5.3 Docker Compose 部署指南》把六服务栈部署起来且 docker compose ps 全部 Up。
  2. 宿主机可对服务执行 docker exec(本文的查看/演示命令都通过它读容器内文件与进程配置)。
  3. 本文只讲配置参数;上传/OCR/检索的业务闭环不在重复,见部署指南 4.7。

三、参数查看与修改方式

3.1 配置分层机制

每个组件启动时,pydantic-settings 按"环境变量覆盖代码默认值"的规则装配出一份 Settings:

  • 字段名与变量名一一对应:字段 papermerge__database__url ↔ 变量 PAPERMERGE__DATABASE__URL(大小写不敏感,双下划线分节);
  • 变量未设置 → 保留 config.py 里的代码默认值;
  • 变量设置为空串 → 生效为空串(如本栈 PAPERMERGE__MAIN__API_PREFIX= 显式置空);
  • 配置在进程启动瞬间读取,改 compose 里的变量必须重建容器才生效(见第七章问题 5)。

变量在启动链路中的消费位置(webapp 镜像 /entrypoint.sh 原文节选,实测读取):

#!/bin/sh

CMD="$1"

mkdir -p /db  # default database is /db/db.sqlite3
...
exec_createsuperuser() {
  cd /auth_server_app && poetry run auth-cli users create --superuser
}

exec_index_schema_apply() {
  echo "exec_index_schema_apply"
  if [ -n "${PAPERMERGE__SEARCH__URL}" ]; then

即 entrypoint 序列:migrate → paper-cli perms sync → 按 PAPERMERGE__AUTH__USERNAME/PASSWORD/EMAIL 自动建超级用户(仅当 PAPERMERGE__SEARCH__URL 非空才做索引 schema apply)→ 生成前端 runtime-config.js → supervisord 拉起 nginx + uvicorn + auth-server。首启建号日志实测为 User admin created(见部署指南 4.4)。

3.2 查看方式一:进程环境变量

看一个服务实际收到了哪些配置变量:

docker exec papermerge-353-stack-webapp-1 sh -c \
  'env | grep PAPERMERGE | sed "s/SECRET_KEY=.*/SECRET_KEY=<REDACTED>/" | sort'

实测回显(本栈 webapp):

PAPERMERGE__AUTH__EMAIL=admin@example.com
PAPERMERGE__AUTH__PASSWORD=q1w2e3r4
PAPERMERGE__AUTH__USERNAME=admin
PAPERMERGE__DATABASE__URL=postgresql://pmuser:q1w2e3r4@db:5432/pmgdb
PAPERMERGE__MAIN__API_PREFIX=
PAPERMERGE__MAIN__MEDIA_ROOT=/var/media/pmg
PAPERMERGE__OCR__AUTOMATIC=false
PAPERMERGE__OCR__DEFAULT_LANG_CODE=deu
PAPERMERGE__OCR__LANG_CODES=deu,eng,ron
PAPERMERGE__REDIS__URL=redis://redis:6379/0
PAPERMERGE__SEARCH__URL=solr://solr:8983/pmg
PAPERMERGE__SECURITY__SECRET_KEY=<REDACTED>

注意两点:PAPERMERGE__OCR__AUTOMATIC=false 是镜像内置默认(compose 并未设置它,env 里却存在);PAPERMERGE__OCR__LANG_CODES 虽被设置,实测在 3.5.3 无任何消费代码(见 5.4 与第七章问题 1)。

3.3 查看方式二:读组件 config.py 原文

各组件的配置文件路径(均在镜像内,实测可 sed/cat 直接读):

组件镜像配置文件
webapppapermerge/papermerge:3.5.3/core_app/papermerge/core/config.py
auth-server(webapp 内置)同上/auth_server_app/auth_server/config.py
ocrworkerpapermerge/ocrworker:0.3.1/app/ocrworker/config.py
i3workerpapermerge/i3worker:0.3/app/i3worker/config.py
docker exec papermerge-353-stack-webapp-1 sed -n '1,80p' /core_app/papermerge/core/config.py

实测回显(webapp Settings 类全文,即默认值的权威出处):

class FileServer(str, Enum):
    LOCAL = 'local'
    S3 = 's3'

class Settings(BaseSettings):
    papermerge__main__logging_cfg: Path | None = Path("/etc/papermerge/logging.yaml")
    papermerge__main__media_root: Path = Path("media")
    papermerge__main__api_prefix: str = ''
    papermerge__main__prefix: str = ''
    papermerge__main__file_server: FileServer = FileServer.LOCAL
    papermerge__main__cf_sign_url_private_key: str | None = None
    papermerge__main__cf_sign_url_key_id: str | None = None
    papermerge__main__cf_domain: str | None = None
    papermerge__main__timezone: str = 'Europe/Berlin'
    papermerge__main__cache_enabled: bool = False
    papermerge__database__url: str = "sqlite:////db/db.sqlite3"
    papermerge__redis__url: str | None = None
    papermerge__ocr__default_lang_code: str = 'deu'
    papermerge__preview__page_size_sm: int = 200  # pixels
    papermerge__ocr__automatic: bool = False
    papermerge__search__url: str | None = None

settings = Settings()

3.4 查看方式三:打印运行时生效值

读 config.py 只能看到默认值;要看"当前进程真正生效的配置",直接在 webapp 内实例化 Settings 打印:

docker exec -i papermerge-353-stack-webapp-1 sh -c 'cd /core_app && poetry run python' <<'PY'
from papermerge.core.config import settings as s
print(s.papermerge__database__url)
print(s.papermerge__search__url)
print(s.papermerge__main__timezone)
PY

实测回显(完整键清单):

papermerge__main__logging_cfg = /etc/papermerge/logging.yaml
papermerge__main__media_root = /var/media/pmg
papermerge__main__api_prefix = 
papermerge__main__prefix = 
papermerge__main__file_server = FileServer.LOCAL
papermerge__main__timezone = Europe/Berlin
papermerge__main__cache_enabled = False
papermerge__database__url = postgresql://pmuser:q1w2e3r4@db:5432/pmgdb
papermerge__redis__url = redis://redis:6379/0
papermerge__search__url = solr://solr:8983/pmg
papermerge__ocr__default_lang_code = deu
papermerge__ocr__automatic = False
papermerge__preview__page_size_sm = 200

i3worker 更省事,官方自带 i3 config 子命令直接打印它生效的四个键(实测回显):

docker exec papermerge-353-stack-i3worker-1 sh -c 'cd /app && poetry run i3 config'
{
  "papermerge__search__url": "solr://solr:8983/pmg",
  "papermerge__redis__url": "redis://redis:6379/0",
  "papermerge__main__logging_cfg": null,
  "papermerge__database__url": "postgresql://pmuser:q1w2e3r4@db:5432/pmgdb"
}

3.5 修改方式与分层证据

持久修改:写进 compose 的 environment:(或 docker run -e),然后 docker compose up -d 重建受影响服务。这是唯一推荐的生产改法。

一次性修改:docker exec -e 只影响那一条命令的进程,用于验证参数效果,不落盘。实测分层证据三连:

1)同一进程环境下去掉业务变量,看代码默认形态:

docker exec -i papermerge-353-stack-webapp-1 sh -c \
  'cd /core_app && env -u PAPERMERGE__DATABASE__URL -u PAPERMERGE__SEARCH__URL \
   -u PAPERMERGE__REDIS__URL -u PAPERMERGE__MAIN__MEDIA_ROOT poetry run python' <<'PY'
from papermerge.core.config import settings as s
print(s.papermerge__database__url)
PY

实测回显(节选,与 3.4 对照):

papermerge__main__media_root = media
papermerge__database__url = sqlite:////db/db.sqlite3
papermerge__redis__url = None
papermerge__search__url = None

——数据库立刻回落到 SQLite 默认、检索回落为未配置,证明"未设 env 保留代码默认"。

2)给单条命令临时注入变量,验证覆盖生效:

docker exec -i -e PAPERMERGE__MAIN__TIMEZONE=Asia/Shanghai \
  -e PAPERMERGE__OCR__DEFAULT_LANG_CODE=eng \
  papermerge-353-stack-webapp-1 sh -c 'cd /core_app && poetry run python' <<'PY'
from papermerge.core.config import settings as s
print(s.papermerge__main__timezone)
print(s.papermerge__ocr__default_lang_code)
PY

实测回显:

papermerge__main__timezone = Asia/Shanghai
papermerge__ocr__default_lang_code = eng

3)i3worker 的 i3 config 同样演示 env 覆盖(实测回显):

docker exec -e PAPERMERGE__SEARCH__URL='solr://solr:8983/demo2' \
  papermerge-353-stack-i3worker-1 sh -c 'cd /app && poetry run i3 config'
{
  "papermerge__search__url": "solr://solr:8983/demo2",
  "papermerge__redis__url": "redis://redis:6379/0",
  "papermerge__main__logging_cfg": null,
  "papermerge__database__url": "postgresql://pmuser:q1w2e3r4@db:5432/pmgdb"
}

四、参数总览

下表汇总四个组件 Settings 类的全部字段(默认值列 = 3.3 读到的 config.py 原文;"文档"列为官方 docs.papermerge.io settings/* 的说法,查询日期 2026-11-01):

变量(省略 PAPERMERGE__ 前缀)消费组件代码默认值作用
SECURITY__SECRET_KEYauth-server必填,无默认JWT 签名密钥(官方文档:必填)
SECURITY__TOKEN_ALGORITHMauth-serverHS256JWT 算法(HS/RS/ES 256/384/512)
SECURITY__TOKEN_EXPIRE_MINUTESauth-server360token 有效期(分钟)
SECURITY__COOKIE_NAMEauth-serveraccess_token会话 Cookie 名
AUTH__USERNAME / PASSWORD / EMAILentrypoint 建号admin / 必填 / admin@example.com首启自动创建超级用户
AUTH__OIDC_*(5 字段)auth-serverNoneOIDC 接入(client_secret/client_id/access_token_url/user_info_url/introspect_url)
AUTH__LDAP_*(5 字段)auth-serverurl None / use_ssl True / dn_format None / email_attr mail / fallback example-ldap.comLDAP 接入
DATABASE__URLwebapp / ocrworker / i3workersqlite:////db/db.sqlite3元数据库;三组件必须一致
DATABASE__POOL_SIZE(Tivoli 专用)5官方文档:仅内部 JWT 校验组件 Tivoli 生效
REDIS__URLwebapp / ocrworker / i3workerNoneCelery broker;webapp 与 worker 必须一致
SEARCH__URLwebapp / i3workerNone(文档称 xapian:////index_db)检索后端;webapp 与 i3worker 必须一致
MAIN__MEDIA_ROOTwebapp / ocrworkermedia文件存储根目录;两者共享同一挂载
MAIN__TIMEZONEwebappEurope/Berlin时区
MAIN__CACHE_ENABLEDwebappFalse缓存开关
MAIN__FILE_SERVERwebapplocallocal / s3(s3 走 CloudFront 签名 URL)
MAIN__CF_SIGN_URL_PRIVATE_KEY / CF_SIGN_URL_KEY_ID / CF_DOMAINwebappNoneFILE_SERVER=s3 时必填的 CloudFront 三件套
MAIN__API_PREFIX / MAIN__PREFIX各组件'' / ''API 前缀 / 队列名前缀(多实例共存用)
MAIN__LOGGING_CFG各组件/etc/papermerge/logging.yaml(worker 侧 None)日志配置 YAML 路径
OCR__DEFAULT_LANG_CODEwebappdeuOCR 默认语言(AUTOMATIC=false 时参与 UI)
OCR__AUTOMATICwebappFalse是否自动 OCR
OCR__LANG_CODES(无消费代码)—(文档默认 deu,eng,ron)官方定位 UI 下拉;实测 3.5.3 死配置(问题 1)
PREVIEW__PAGE_SIZE_SMwebapp200缩略图像素
OCR_WORKER_ARGSocrworker 启动脚本—celery 参数,如 -Q ocr -c 2
I3_WORKER_ARGSi3worker 启动脚本—celery 参数,如 -Q i3 -c 2
aws_access_key_id / aws_secret_access_key / aws_region_name / S3__BUCKET_NAMEocrworkerNoneocrworker 的 S3 存储后端

官方文档口径(settings/overview,查询日期 2026-11-01):全部变量里只有 PAPERMERGE__SECURITY__SECRET_KEY 与 PAPERMERGE__AUTH__PASSWORD 是必填——与实测一致:auth-server 的 config.py 里 secret_key 与 database_url 无默认值,缺失即启动失败;其余都有代码默认。

五、核心参数分类详解

5.1 DATABASE__URL:SQLite ↔ PostgreSQL 切换

三种格式(官方文档 + 本文实测):

postgresql://USER:PASSWORD@HOST:PORT/NAME
mysql://myuser:mypass@db:3306/paperdb
sqlite:////db/db.sqlite3        (代码默认,注意四斜杠=绝对路径)
  • 不设该变量时 webapp 生效值即 sqlite:////db/db.sqlite3(3.5 实测回显),entrypoint 还会 mkdir -p /db——这就是"单容器快速演示"形态的落库方式;
  • 生产切换只需在 webapp/ocr_worker/i3worker 三个服务里设置同一个 URL(官方文档三处 Note 反复强调)。本栈 compose 对三者统一设置 postgresql://pmuser:q1w2e3r4@db:5432/pmgdb,3.4 打印的生效值证实 webapp 与 i3worker 均已切到 PostgreSQL;
  • 切换后首次启动自动跑 alembic 迁移(部署指南 4.4 日志),空库直接可用;SQLite→PostgreSQL 的存量迁移 3.x 未提供官方工具,需自行导出导入。
  • DATABASE__POOL_SIZE 默认 5,官方文档明确仅对 Tivoli(内部 JWT 校验组件,SQLAlchemy ORM)生效,webapp 的 Django ORM 不受它影响。

5.2 SEARCH__URL:xapian ↔ solr 切换

两种后端(官方文档 settings/search,查询日期 2026-11-01):

xapian:///PATH     例:xapian:////index_db(文档称默认值)
solr://HOST:PORT/INDEX   例:solr://solr:8983/pmg

实测要点:

  1. 代码默认与文档不一致:webapp config.py 里 papermerge__search__url: str | None = None(3.3 原文),3.5 实测去掉 env 后打印的也是 None;官方文档"默认 xapian:////index_db"是部署形态描述(entrypoint 仅当该变量非空才做索引 schema apply)。以代码为准。
  2. i3worker 的 SEARCH__URL 是必填字段(config.py 原文 papermerge__search__url: str,无默认),且镜像内没有 xapian 后端——实测给 i3worker 设 PAPERMERGE__SEARCH__URL=xapian:////tmp/idx 后 i3 config 直接崩:
ModuleNotFoundError: No module named 'salinic.backends.xapian'

结论:用 i3worker 就必须用 Solr;xapian 只存在于 webapp 侧的本地检索形态。 3. webapp 与 i3worker 必须同值(官方 Note;本栈两处 compose env 同为 solr://solr:8983/pmg,3.4 的 i3 config 打印与 webapp 生效值互证)。 4. 切到 Solr 后 core 的 schema 有坑(copyField 缺失导致全文检索恒 0 命中),补丁做法见部署指南 4.1 与第九章问题 4。

5.3 REDIS__URL

格式 redis://HOST:PORT/NUMBER(例 redis://redis:6379/0)。webapp/ocrworker/i3worker 三方必须一致(官方 Note + 本栈 compose 统一设置,3.4/3.5 打印互证)。Redis 是 Celery broker 兼任务结果存储:OCR/i3 任务状态以 celery-task-meta-* 键落在其中(部署指南 8.5)。

5.4 OCR 三参数与"真实白名单"

  • OCR__AUTOMATIC(默认 False):False=OCR 由用户手动调度(上传时勾选或事后触发 POST /api/tasks/ocr);True=后台调度自动跑。官方文档原文提醒 False 才适合"终端用户即管理员"的小实例。本栈 compose 显式设 false。
  • OCR__DEFAULT_LANG_CODE(默认 deu):手动 OCR 时的默认语言。实测确有消费代码(webapp 内 grep):
/core_app/papermerge/core/features/nodes/router.py:123:            pynode.lang = settings.papermerge__ocr__default_lang_code
/core_app/papermerge/core/config.py:24:    papermerge__ocr__default_lang_code: str = 'deu'
  • OCR__LANG_CODES:官方文档定位是"UI 下拉展示哪些语言,worker 里有没有由你负责",默认 deu,eng,ron。实测 3.5.3 是死配置——webapp 全代码树 grep lang_codes(大小写不敏感)零命中:
docker exec papermerge-353-stack-webapp-1 sh -c \
  'grep -rni "lang_codes" /core_app/papermerge | grep -v ".pyc" | head -20'
# 实测回显:(空)

ocrworker、i3worker 同样零命中。写了不报错、也不起作用(第七章问题 1)。

OCR 语言的真实约束是两层(均实测):

  1. API 校验白名单:POST /api/tasks/ocr 的 lang 只接受 schema 硬编码的 24 个 ISO 639-2 码,传 zzz 实测 422:
{"detail":[{"type":"literal_error","loc":["body","lang"],"msg":"Input should be 'ces', 'dan', 'deu', 'ell', 'eng', 'fas', 'fin', 'fra', 'guj', 'heb', 'hin', 'ita', 'jpn', 'kor', 'lit', 'nld', 'nor', 'pol', 'por', 'ron', 'san', 'spa', 'kaz' or 'rus'","input":"zzz","ctx":{"expected":"'ces', 'dan', 'deu', 'ell', 'eng', 'fas', 'fin', 'fra', 'guj', 'heb', 'hin', 'ita', 'jpn', 'kor', 'lit', 'nld', 'nor', 'pol', 'por', 'ron', 'san', 'spa', 'kaz' or 'rus'"}}]}
  1. 镜像内 tesseract 语言包:ocrworker 实测只有 5 个:
List of available languages in "/usr/share/tesseract-ocr/5/tessdata/" (5):
deu
eng
osd
ron
spa

两层取交集,本镜像实际可用 OCR 语言 = deu / eng / ron / spa。白名单内但缺语言包的码(如 jpn):API 受理(返回 null),运行时 ocrmypdf 报 MissingDependencyError → celery 任务 FAILURE——"受理≠能跑"。

5.5 SECURITY 与 AUTH(auth-server)

auth-server 的 Settings 原文(/auth_server_app/auth_server/config.py,实测读取):

class Settings(BaseSettings):
    papermerge__security__secret_key: str
    papermerge__security__token_algorithm: Algs = Algs.HS256
    papermerge__security__token_expire_minutes: int = 360
    papermerge__security__cookie_name: str = "access_token"

    # database where to read user table from
    papermerge__database__url: str

要点:

  • SECURITY__SECRET_KEY 必填无默认(官方文档:大随机值、用于签发 JWT,泄露即可伪造登录态)。官方文档示例变量名写作 PAPERMERGE__SECRET__SECRET_KEY,与代码字段 papermerge__security__secret_key 不符——以代码为准,本栈用 PAPERMERGE__SECURITY__SECRET_KEY 实测有效。
  • SECURITY__TOKEN_EXPIRE_MINUTES 默认 360:实测新签 token 的 exp 与签发时刻差 21599 秒 ≈ 359 分钟(解码误差内吻合)。改 7 天即 PAPERMERGE__SECURITY__TOKEN_EXPIRE_MINUTES=10080(官方文档示例)。
  • AUTH__USERNAME/PASSWORD/EMAIL:首启 entrypoint 建超级用户用(3.1),密码必填;实测日志 User admin created。
  • OIDC:代码字段 5 个(client_secret/client_id/access_token_url/user_info_url/introspect_url),官方文档另列 authorize_url/logout_url/scope/redirect_url 等键;接 Keycloak/Authelia 属 Cloud/进阶形态,本文未实测。
  • LDAP:代码字段 5 个——ldap_url(非空即启用 LDAP 认证,不带 ldap:// 前缀)、ldap_use_ssl(默认 True)、ldap_user_dn_format(uid={username},ou=... 占位格式)、ldap_email_attr(默认 mail)、ldap_user_email_domain_fallback(默认 example-ldap.com,取不到邮箱时拼 <username>@<fallback>)。
  • REMOTE:官方文档另有 AUTH__REMOTE 头/Cookie 透传认证(Remote-User 等),配合外置反代认证使用,本文未实测。

5.6 MAIN 节

  • MAIN__MEDIA_ROOT:默认相对路径 media(3.5 实测),生产必须给绝对路径并与 ocrworker 共享同一挂载(本栈 /var/media/pmg ↔ bind mount ./data/media)。盘上按 UUID 前 4 hex 两级分片:原件在 docvers/xx/yy/<版本UUID>/,OCR 产物在 ocr/pages/xx/yy/<页UUID>/page.pdf+page.txt(布局实测见部署指南第五章)。
  • MAIN__TIMEZONE:默认 Europe/Berlin(官方文档示例同值),中文环境建议改 Asia/Shanghai(3.5 已演示覆盖生效)。
  • MAIN__FILE_SERVER:local(默认,webapp 直接发文件)或 s3(AWS CloudFront 签名 URL 分发,需同时配 CF_SIGN_URL_PRIVATE_KEY/CF_SIGN_URL_KEY_ID/CF_DOMAIN 三键,官方文档明示目前仅支持 CloudFront)。
  • MAIN__API_PREFIX / MAIN__PREFIX:前者影响 REST 路径前缀(本栈显式置空),后者影响 Celery 队列名前缀——设 PREFIX=demo 时队列变 demo_ocr/demo_i3,worker 的 -Q 参数要跟着改(官方文档 ocrworker/i3worker 页 Note)。
  • MAIN__CACHE_ENABLED:默认 False。
  • PREVIEW__PAGE_SIZE_SM:默认 200 像素(列表缩略图尺寸)。
  • MAIN__LOGGING_CFG:默认 /etc/papermerge/logging.yaml,镜像内该文件实测全文:
version: 1
disable_existing_loggers: false

formatters:
  verbose:
    format: '%(levelname)s %(asctime)s %(module)s %(message)s'

handlers:
  console:
    level: INFO
    class: logging.StreamHandler
    formatter: verbose

loggers:
  auth_server:
    level: INFO
    handlers: [console]
  papermerge.search.tasks:
    level: INFO
    handlers: [console]
    propagate: no

要调日志级别/输出,bind mount 一份自己的 YAML 并把 LOGGING_CFG 指过去即可(标准 Python logging dictConfig 格式)。

5.7 worker 专属参数

  • OCR_WORKER_ARGS / I3_WORKER_ARGS:不是 PAPERMERGE__ 变量,而是原样传给 celery worker 的启动参数。官方文档建议 "-Q ocr -c 2" / "-Q i3 -c 2"(队列名 ocr/i3,与 MAIN__PREFIX 联动)。本栈实测启动横幅 [queues] .> ocr exchange=ocr(direct) key=ocr(部署指南 4.4)。
  • ocrworker 的 S3 存储四键(aws_access_key_id/aws_secret_access_key/aws_region_name/S3__BUCKET_NAME,config.py 原文见第四章表):ocrworker 支持本地图或 S3 两种存储后端(官方文档 settings/ocrworker),配了就走 S3,本文未实测。
  • i3worker 的 config.py 全文只有四个字段(search__url 必填、redis__url、logging_cfg、database__url),职责单一:把数据库内容同步进索引。

六、生产配置模板

综合实测结论,生产最小正确集(六服务 compose 的 environment: 摘录,与部署指南 4.2 一致):

# webapp / ocr_worker / i3worker 三者必须一致的键
PAPERMERGE__DATABASE__URL: postgresql://<user>:<pass>@db:5432/pmgdb
PAPERMERGE__REDIS__URL: redis://redis:6379/0
# webapp / i3worker 必须一致
PAPERMERGE__SEARCH__URL: solr://solr:8983/pmg
# webapp / ocr_worker 必须一致且为绝对路径
PAPERMERGE__MAIN__MEDIA_ROOT: /var/media/pmg
# 必填
PAPERMERGE__SECURITY__SECRET_KEY: <64位随机串>
PAPERMERGE__AUTH__PASSWORD: <强密码>
# 建议按环境调整
PAPERMERGE__MAIN__TIMEZONE: Asia/Shanghai
PAPERMERGE__OCR__DEFAULT_LANG_CODE: <你的主要文档语言>
OCR_WORKER_ARGS: "-Q ocr -c 2"
I3_WORKER_ARGS: "-Q i3 -c 2"

一致性矩阵(错配的典型后果):

键必须同步的组件错配后果
DATABASE__URLwebapp、ocr_worker、i3workerworker 读写错库,OCR 结果不可见
REDIS__URLwebapp、ocr_worker、i3worker任务投递进空队列,OCR 永不执行
SEARCH__URLwebapp、i3worker一边查 A 核一边写 B 核,检索恒 0 命中
MAIN__MEDIA_ROOTwebapp、ocr_workerOCR worker 找不到原件文件
MAIN__PREFIX全部 + WORKER_ARGS 的 -Q队列名对不上,同上

七、常见问题

问题 1:PAPERMERGE__OCR__LANG_CODES 配了没效果

3.5.3 实测三个组件代码树 grep lang_codes 全部零命中(5.4),它是死配置:不报错、不生效。官方文档把它定位为"UI 下拉展示",且明确语言可用性由 worker 自担责任。实际约束在 API 硬编码 24 码白名单 + 镜像 tesseract 语言包(5.4),可用交集为 deu/eng/ron/spa。要加语言,得往 ocrworker 镜像里补 tessdata 并确认码在 24 白名单内。

问题 2:OCR 任务返回 null 但没出文本

POST /api/tasks/ocr 受理即返回 null(HTTP 200),不代表能跑成功。实测传白名单内但缺语言包的 jpn:任务被 celery 接收后 ocrmypdf 报 MissingDependencyError,Redis celery-task-meta-* 状态 FAILURE。排查顺序:先核对 5.4 的两层白名单,再看 Redis 任务元数据与 media 盘产物(部署指南 8.5)。

问题 3:i3worker 配 xapian 起不来

i3worker 的 SEARCH__URL 必填且镜像未安装 xapian 后端,实测设 xapian:////tmp/idx 直接 ModuleNotFoundError: No module named 'salinic.backends.xapian'(5.2)。部署了 i3worker(要 Solr 全文检索)就只能用 solr://。

问题 4:官方文档默认值与代码默认值不一致

两处实测不一致:SEARCH__URL(文档 xapian:////index_db vs 代码 None)、SECURITY__SECRET_KEY 的示例变量名(文档 PAPERMERGE__SECRET__SECRET_KEY vs 代码字段 security 节)。以镜像内 config.py 为准(3.3 的读法),文档当导读。

问题 5:改了 compose 环境变量但"没生效"

pydantic-settings 只在进程启动时读一次 env。改完必须 docker compose up -d(变量变化会重建对应容器);docker exec -e 是一次性的(3.5),不会改变常驻进程的配置。验证生效用 3.4 的打印法,别看 compose 文件猜。

问题 6:token 6 小时就过期

默认 TOKEN_EXPIRE_MINUTES=360(5.5 实测吻合)。长任务脚本要么按周期重登 POST /api/token,要么调大该变量(如 10080=7 天,官方文档示例)。注意它属于 auth-server 配置,改后需重建 webapp 容器。

八、参考链接