Docspell 0.43.0 配置参数详解
Docspell 0.43.0 双服务配置体系实测:HOCON 三层加载与环境变量/配置文件优先级、restserver+joex 关键参数逐项详解、JVM 堆限 256-512MB 验证与生产配置模板
Docspell 0.43.0 配置参数详解
目标:把 Docspell 0.43.0 两个核心服务(restserver 与 joex)的配置体系讲透——三层参数加载机制、环境变量映射规则、配置文件挂载的实测优先级行为,以及两服务 reference.conf 中数百个参数里最关键的几十个默认值与修改方法。文中所有配置项均取自 0.43.0 内置 reference.conf 实提取件,所有覆盖效果均在真实部署环境逐条实测(2026-11-01),环境搭建过程见《Docspell Docker Compose 部署指南》。
一、环境信息
| 项目 | 值 | 验证命令 |
|---|---|---|
| 操作系统 | Ubuntu 26.04.1 LTS | cat /etc/os-release |
| Docspell | v0.43.0(builtAtString 2025-03-15 16:46:56.280+0100,gitCommit 6b4e03ac3a2ac013783ec42245c01b7c0ff0dca9) | curl http://localhost:8137/api/info/version |
| restserver | 配置命名空间 docspell.server.*,服务端口 7880(宿主映射 8137) | curl -s http://localhost:8137/api/info/version |
| joex | 配置命名空间 docspell.joex.*,服务端口 7878(仅内部网络可达) | 见 3.1 jps 回显 |
| 运行时 | OpenJDK 21(两服务均为 JVM 进程,堆参数 -Xms256m -Xmx512m) | jps -v |
| 数据库 | PostgreSQL(jdbc:postgresql://db:5432/docspell) | 见 5.6 实测 |
| 全文检索 | Solr(http://docspell-solr:8983/solr/docspell) | 见 5.8 实测 |
| 默认参数文件 | restserver reference.conf 534 行、joex reference.conf 940 行(本文默认值均实提取自这两份文件) | 见 2.2 |
版本接口实测回显:
$ curl -s http://localhost:8137/api/info/version
{"version":"0.43.0","builtAtMillis":1742053616280,"builtAtString":"2025-03-15 16:46:56.280+0100","gitCommit":"6b4e03ac3a2ac013783ec42245c01b7c0ff0dca9","gitVersion":"0.43.0"}
💡 技术拓展:Docspell 的配置格式是 HOCON(Human-Optimized Config Object Notation)。同类配置文件格式还有 Java Properties、JSON、YAML、TOML、INI 等;HOCON 是 JSON 的超集,支持注释、多行字符串、
a.b.c路径压缩写法与${...}值替换,由 Typesafe Config(Lightbend)参考实现。Docspell 两服务均基于 Typesafe Config 加载,因此本章所有规则(合并、覆盖、环境变量映射)都是 Typesafe Config 的标准行为。
二、前置条件
2.1 服务与工具
- 一个已按《Docspell Docker Compose 部署指南》部署完成、5 个服务全部正常的 Docspell 0.43.0 栈(restserver、joex、目录监视、PostgreSQL、Solr),对外入口
http://localhost:8137; - 已注册示例账号
admin/ 密码q1w2e3r4; - 宿主机具备
curl、python3(用于解析 JSON 回显)。
2.2 获取两服务的内置 reference.conf
Docspell 每个 jar 包内都捆绑了完整的默认参数文件 reference.conf(restserver 534 行、joex 940 行),所有"默认值"以它为准。从安装目录 lib/ 下的构件 jar(如 com.github.eikek.docspell-restserver-0.43.0.jar)中提取即可:
$ unzip -o com.github.eikek.docspell-restserver-0.43.0.jar reference.conf -d rc/
$ unzip -o com.github.eikek.docspell-joex-0.43.0.jar reference.conf -d rc/
两份提取件行数实测 534 / 940 行,本文第四章参数总览表与第五章默认值全部逐项对照这两份实提取件书写。
三、参数查看与修改方式
3.1 三层加载模型与启动命令行验证
Typesafe Config 按优先级从低到高分三层合并配置:
- 内置 reference.conf(jar 内,只读,提供全部默认值);
- 配置文件(通过启动第一个参数或
-Dconfig.file=指定的 HOCON 文件,覆盖 reference); - 环境变量
DOCSPELL_*(映射规则见 3.3)。
实际命令行参数用 jps -v 验证(restserver 与 joex 两个 JVM 服务分别执行)。注意:服务运行环境默认只带 JRE 子集(java/keytool 等),没有 jps,需先补装 JDK 工具:
$ apt-get install -y --no-install-recommends openjdk-21-jdk-headless
$ jps -v
304 Jps -Dapplication.home=/usr/lib/jvm/java-21-openjdk-amd64 -Xms8m -Djdk.module.main=jdk.jcmd
1 Main -Xms256m -Xmx512m -Dconfig.file=/opt/docspell-restserver/bin/../conf/docspell-server.conf
joex 侧回显:
$ jps -v
8 Main -Xms256m -Xmx512m -Dconfig.file=/opt/docspell-joex/bin/../conf/docspell-joex.conf
190 Jps -Dapplication.home=/usr/lib/jvm/java-21-openjdk-amd64 -Xms8m -Djdk.module.main=jdk.jcmd
两点信息一目了然:-Xms256m -Xmx512m 即生效的 JVM 堆参数(来源环境变量 JAVA_OPTS);-Dconfig.file=… 指向启动脚本自带的 conf 文件——该文件不存在时即回落"内置 reference.conf + 环境变量"层,这也是本部署未挂自定义配置文件时的实际形态。
3.2 配置文件挂载实测:环境变量层会被完全忽略(本文最重要发现)
官方推荐的"参数多"配置方式是把 HOCON 文件路径作为启动命令的第一个参数传入,落到编排配置即:
restserver:
volumes:
- ./docspell-server.conf:/opt/docspell.conf
command:
- /opt/docspell.conf
第一次实测踩坑:只在新文件里写少量覆盖键(app-name、max-item-page-size 等),原 compose 的环境变量(jdbc、bind、solr 地址等)全部保留不动。结果服务启动后宿主 8137 入口失联——进程实际回落到 reference.conf 默认值:监听 bind.address = "localhost"、数据库用内嵌 H2(jdbc:h2:…/docspell-demo.db),DOCSPELL_SERVER_* 环境变量一个都不再参与。
⚠️ 注意:这不是"同名键以文件为准",而是传了配置文件参数后,环境变量这一整层被完全跳过——启动脚本把环境变量折叠进它生成的那个 conf 文件,命令行参数一传,生成的文件就被顶掉了。因此配置文件必须自包含:凡是不想回落默认值的键(jdbc、bind.address、base-url、internal-url、full-text-search、signup 等)都要写全。
把必需键补全后重新强制重建服务(只改挂载文件内容不触发重建,需 up -d --force-recreate),全部生效,实测回显如下。
测试文件 docspell-server.conf(完整,可直接当生产模板用,见第六章):
docspell.server {
app-name = "Docspell-C1"
bind.address = "0.0.0.0"
base-url = "http://localhost:8137"
internal-url = "http://docspell-restserver:7880"
max-item-page-size = 5
auth {
server-secret = "q1w2e3r4"
session-valid = "10 minutes"
}
admin-endpoint.secret = "q1w2e3r4"
integration-endpoint {
enabled = true
http-header {
enabled = true
header-name = "Docspell-Integration"
header-value = "q1w2e3r4"
}
}
full-text-search {
enabled = true
backend = "solr"
solr.url = "http://docspell-solr:8983/solr/docspell"
}
backend {
jdbc {
url = "jdbc:postgresql://db:5432/docspell"
user = "docspell"
password = "q1w2e3r4"
}
signup.mode = "open"
addons.enabled = false
}
}
生效验证一:session-valid = "10 minutes"——登录返回的 validMs 从默认 300000 变为 600000:
$ curl -s -X POST http://localhost:8137/api/v1/open/auth/login -H "Content-Type: application/json" -d "{\"account\":\"admin\",\"password\":\"q1w2e3r4\"}"
{"collective": "admin", "user": "admin", "success": true, "message": "Login successful", "token": "<token>", "validMs": 600000, "requireSecondFactor": false}
生效验证二:max-item-page-size = 5——搜索响应 limit 被压到 5,且客户端请求 limit=50 时封顶并标记 limitCapped: True:
$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=" # 响应尾部
groups: [('2026-11', 5)] limit: 5 offset: 0 limitCapped: False
$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=&limit=50"
limit: 5 limitCapped: True
生效验证三:文件里的 backend.jdbc 指向 PostgreSQL,原有数据完好(若回落 H2 空库,这里会是全零):
$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/collective/insights
{"incomingCount":5,"outgoingCount":1,"deletedCount":0,"itemSize":26861,"tagCloud":{"items":[{"tag":{"id":"BWfHJnZ9ipt-3NeKsaW4bmr-Pqj6fZvWZeP-ZhDmwU4yrEq","name":"invoice","category":null,"created":1793537206738},"count":2}]}}
生效验证四:文件里的 admin-endpoint.secret 生效,管理端点可用:
$ curl -s -X POST -H "Docspell-Admin-Secret: q1w2e3r4" http://localhost:8137/api/v1/admin/fts/reIndexAll
{"success":true,"message":"Full-text index will be re-created."}
3.3 环境变量映射规则
不传配置文件时,docspell.server.* 的键可用环境变量设置,映射规则两条:
- 点
.→ 单下划线_ - 短横线
-→ 双下划线__
| 配置键 | 环境变量 |
|---|---|
docspell.server.bind.address | DOCSPELL_SERVER_BIND_ADDRESS |
docspell.server.auth.server-secret | DOCSPELL_SERVER_AUTH_SERVER__SECRET |
docspell.server.admin-endpoint.secret | DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET |
docspell.server.full-text-search.solr.url | DOCSPELL_SERVER_FULL__TEXT__SEARCH_SOLR_URL |
docspell.server.integration-endpoint.http-header.header-value | DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE |
docspell.joex.text-analysis.nlp.mode | DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE |
💡 说明:
server-secret映射为SERVER__AUTH段里的SERVER__SECRET?不是——注意只有键名里的连字符变双下划线,层级点仍是单下划线:auth.server-secret→AUTH_SERVER__SECRET。写错一个下划线该变量就静默无效,这是环境变量方式最常见的坑。
3.4 文件与环境变量同名键优先级实测
在挂载文件(max-item-page-size = 5、session-valid = "10 minutes")的同时,额外设置环境变量 DOCSPELL_SERVER_MAX_ITEM__PAGE_SIZE=7,重启后观察:
$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=" # 观察 limit
limit: 5 limitCapped: False
$ curl -s -X POST http://localhost:8137/api/v1/open/auth/login -H "Content-Type: application/json" -d "{\"account\":\"admin\",\"password\":\"q1w2e3r4\"}" # 观察 validMs
validMs: 600000
limit 取文件值 5 而非环境变量值 7——与 3.2 结论一致:传了配置文件后环境变量层整体失效,不存在"环境变量作覆盖层"的行为。撤掉配置文件恢复纯环境变量模式后,默认值回归(max-item-page-size=200、session-valid=5 minutes):
$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=" # 默认 max-item-page-size=200、session-valid=5min
limit: 200
validMs: 300000
3.5 参数查看渠道汇总
| 渠道 | 内容 | 适用 |
|---|---|---|
内置 reference.conf(2.2 提取) | 全部键 + 注释 + 默认值 | 查默认值、查合法取值 |
jps -v / ps -ef | 实际命令行(JAVA_OPTS、config.file) | 验证 JVM 与文件挂载 |
行为反证(如 3.2 的 validMs/limit) | 参数实际生效值 | 验证覆盖是否成功 |
版本接口 /api/info/version | 构建版本与 commit | 确认参数文件与二进制同版本 |
四、参数总览
4.1 restserver(docspell.server.*)关键项
| 键 | 默认值 | 说明 |
|---|---|---|
app-name | "Docspell" | Web 界面右上角显示名 |
app-id | "rest1" | 节点 id,多实例必须唯一 |
base-url | "http://localhost:7880" | 对外访问地址,用于生成绝对 URL 与 cookie;默认不改时按请求 HOST 推断 |
internal-url | "http://localhost:7880" | 集群内部互相访问地址(joex 回调本服务用) |
bind.address / bind.port | "localhost" / 7880 | 监听地址;对外提供服务必须改 0.0.0.0 |
server-options.max-connections | 1024 | 最大并发连接 |
server-options.response-timeout | 45s | 首字节响应超时 |
max-item-page-size | 200 | 搜索单页硬上限(实测见 3.2) |
max-note-length | 180 | 搜索结果中条目笔记返回字符数上限 |
show-classification-settings | true | 是否向用户展示分类设置表单 |
auth.server-secret | "" | token 签名密钥;为空则每次启动随机生成(重启即全员掉线,生产必设) |
auth.session-valid | "5 minutes" | token 有效期(实测见 3.2) |
auth.remember-me.valid | "30 days" | 记住我 token 有效期 |
download-all.max-files / max-size | 500 / 1400M | 打包下载限制 |
admin-endpoint.secret | "" | 管理端点密钥;空 = 端点禁用 |
integration-endpoint.enabled | false | 集成上传端点开关 |
full-text-search.enabled / backend | false / "solr" | 全文检索开关与引擎 |
backend.jdbc.url | H2 文件库 | 元数据库连接(见 5.6) |
backend.signup.mode | "open" | 注册策略 open/invite/closed(见 5.3) |
backend.files.chunk-size | 524288 | 文件分块字节数(见 5.7) |
backend.addons.enabled | false | 插件执行开关 |
4.2 joex(docspell.joex.*)关键项
| 键 | 默认值 | 说明 |
|---|---|---|
app-id | "joex1" | 节点 id,多 joex 必须唯一 |
base-url / bind | "http://localhost:7878" / localhost:7878 | 状态查看 REST 接口 |
jdbc.url | H2 文件库 | 必须与 restserver 同一数据库 |
scheduler.pool-size | 1 | 并行作业数 |
scheduler.counting-scheme | "4,1" | 高/低优先级作业配比 |
scheduler.retries / retry-delay | 2 / "1 minute" | 失败重试次数与指数退避基数 |
scheduler.wakeup-period | "30 minutes" | 空队列自唤醒周期(重试卡住作业) |
house-keeping.schedule | "Sun *-*-* 00:00:00 UTC" | 清理任务 cron |
house-keeping.cleanup-jobs.older-than | "30 days" | 作业日志保留期 |
extraction.pdf.min-text-len | 500 | PDF 原生文本短于此则补跑 OCR |
extraction.preview.dpi | 32 | 预览图渲染 dpi |
extraction.ocr.max-image-size | 14000000 | OCR 像素上限(height×width) |
extraction.ocr.page-range.begin | 10 | OCR 只处理前 10 页 |
text-analysis.max-length | 5000 | 参与分析的文本长度上限 |
text-analysis.nlp.mode | full | NLP 模式(堆不足会 OOM,见 7.1) |
text-analysis.classification.item-count | 600 | 分类器训练样本条目数 |
convert.html-converter | "wkhtmlpdf" | HTML→PDF 工具选择 |
convert.ocrmypdf.enabled | true | PDF 加文本层/PDF-A 转换 |
convert.decrypt-pdf.enabled / passwords | true / [] | 加密 PDF 解密尝试 |
files.chunk-size / default-store | 524288 / "database" | 必须与 restserver 一致 |
full-text-search.* | 同 restserver | 必须与 restserver 一致 |
addons.executor-config.runner | "nix-flake, docker, trivial" | 插件运行器候选列表 |
五、核心参数分类详解
5.1 服务身份与监听
app-name 只影响 Web 界面右上角显示,实测把文件里 app-name = "Docspell-C1" 挂载生效后界面即显示新名(3.2 同一文件)。base-url 与 internal-url 是一对易混键:前者是浏览器用户看到的地址(生成分享链接、cookie 域),后者是其他节点(joex、目录监视客户端)回调本服务用的内部地址。本部署的实测组合:
base-url = "http://localhost:8137"
internal-url = "http://docspell-restserver:7880"
bind.address = "0.0.0.0"
bind.address 默认 "localhost"——这正是 3.2 第一次踩坑时服务"失联"的直接原因:环境变量 DOCSPELL_SERVER_BIND_ADDRESS=0.0.0.0 被忽略,进程只绑了回环地址。
server-options 三项(enable-http-2=false、max-connections=1024、response-timeout=45s)对中小部署无需调整;上传大文件超时才需要动 response-timeout。
5.2 认证与会话(auth.*)
auth.server-secret:签名 token 的密钥。默认为空 = 每次启动随机生成,意味着服务重启后所有已发 token 作废、网页端全部掉线;多 restserver 实例必须共享同一 secret。实测设为"q1w2e3r4"(3.2 文件)后,重启前后 token 行为稳定。官方注释支持b64:/hex:前缀的强随机密钥,生产建议用之。auth.session-valid:token 有效期,默认"5 minutes",Web 界面会自动周期换新 token。实测改"10 minutes"后登录响应validMs: 600000(3.2 验证一),默认形态validMs: 300000(3.4 回归)。脚本党用 API 时,这个值就是 token 的"续命周期"。auth.remember-me:enabled=true、valid="30 days",登录勾选记住我后的长效 token;joex 的house-keeping.cleanup-remember-me(默认older-than="30 days")负责清理过期记录,两处时长要配套。
💡 技术拓展:Docspell 的 token 是自包含签名串(登录回显可见
时间戳-base64(账号信息)-$2a$10$…bcrypt 签名段),服务端不存会话表。同类方案有:服务端会话表(有状态,可即时吊销)、JWT(标准三方库多,但同样难即时吊销)、不透明随机 token 查表(可吊销、需存储)。Docspell 选签名式,代价是 logout 后旧 token 在有效期内仍可用(实测证据见《Docspell 0.43.0 API 使用详解》4.4)。
5.3 注册策略(backend.signup.*)
三态实测闭环。默认 mode = "open"(人人可注册新集体):
$ curl -s -X POST http://localhost:8137/api/v1/open/signup/register -H "Content-Type: application/json" -d "{\"collectiveName\":\"guest\",\"login\":\"guest\",\"password\":\"q1w2e3r4\"}"
{"success":true,"message":"Signup successful"}
改 mode = "closed" 后注册被拒,但既有账号登录不受影响:
$ curl -s -X POST http://localhost:8137/api/v1/open/signup/register -H "Content-Type: application/json" -d "{\"collectiveName\":\"guest2\",\"login\":\"guest2\",\"password\":\"q1w2e3r4\"}"
{"success":false,"message":"Sorry, registration is closed."}
$ curl -s -X POST http://localhost:8137/api/v1/open/auth/login -H "Content-Type: application/json" -d "{\"account\":\"admin\",\"password\":\"q1w2e3r4\"}"
{"collective": "admin", "user": "admin", "success": true, "message": "Login successful", "token": "<token>", "validMs": 300000, "requireSecondFactor": false}
第三态 mode = "invite" 需配 new-invite-password(非空),注册必须携带邀请码;邀请 token 有效期 invite-time = "3 days",过期未用的邀请由 joex house-keeping.cleanup-invites(older-than="30 days")兜底清理——注释明确要求 joex 的清理时长必须大于 restserver 的 invite-time。
⚠️ 注意:
closed只关"新注册",不关"加成员"。已登录用户仍可通过POST /sec/user给本集体加成员(实测见《Docspell 0.43.0 API 使用详解》5.1),所以单机自用场景open的风险主要是陌生人建自己的集体占资源,而非入侵你的数据。
5.4 分页与响应体量限制
max-item-page-size(默认 200)是服务端对搜索单页的硬封顶:客户端传更大 limit 时响应 limitCapped: true(3.2 验证二实测 5 封顶 limit=50)。低内存部署把它调小(如 50)可显著压住单次响应体积。max-note-length(默认 180)限制搜索结果里每条笔记的字符数,防止长笔记把响应撑爆——需要全文时走单条目详情接口。
5.5 管理端点与集成端点
admin-endpoint:secret 为空整个 /api/v1/admin/* 禁用;非空则请求必须带 Docspell-Admin-Secret 头。实测无头调用 403:
$ curl -s -X POST http://localhost:8137/api/v1/admin/fts/reIndexAll (无 secret 头)
HTTP/1.1 403 Forbidden
Date: Sun, 01 Nov 2026 12:44:13 GMT
Connection: keep-alive
带头成功(3.2 验证四)。该端点族含 reIndexAll(全集体重建全文索引)、resetPassword 等,属运维后门,secret 强度按生产密钥对待。
integration-endpoint:默认 enabled=false,专为"本机其他软件往 Docspell 灌文件"设计。三种保护方式可叠加(allowed-ips 白名单 / http-basic / http-header),官方注释警告:全部保护都关等于裸奔。实测本部署采用 header 方式(header-name = "Docspell-Integration" + 自定义值),带对值上传成功、带错值 403,完整回显见《Docspell 0.43.0 API 使用详解》5.8。source-name = "integration" 决定入库条目的 source 字段值(实测条目 source=integration 与此一致);priority = "low" 决定作业队列优先级。
5.6 元数据库(backend.jdbc / joex.jdbc)
reference.conf 默认是 H2 文件库:
url = "jdbc:h2://"${java.io.tmpdir}"/docspell-demo.db;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;AUTO_SERVER=TRUE"
user = "sa"
password = ""
💡 技术拓展:Docspell 支持三种 JDBC 后端——H2(内嵌文件库,零运维,官方定位个人与中等规模可用,多节点靠
AUTO_SERVER=TRUE共享同一文件)、PostgreSQL(生产推荐,独立服务)、MariaDB/MySQL(兼容支持)。全文检索侧另有 Solr / PostgreSQL 两引擎(见 5.8),与元数据库选择相互独立。
生产形态实测用 PostgreSQL(3.2 文件 backend.jdbc 三键),要点:
- restserver 用
docspell.server.backend.jdbc.*,joex 用docspell.joex.jdbc.*——两处必须指向同一库,否则作业与数据分裂; - 环境变量路径对应
DOCSPELL_SERVER_BACKEND_JDBC_URL/USER/PASSWORD与DOCSPELL_JOEX_JDBC_URL/USER/PASSWORD六件套; - 首次启动自动跑 Flyway 建表(
database-schema.run-main-migrations=true),repair-schema=false保持默认。
5.7 文件存储(backend.files.*)
文件本体按 chunk-size(默认 524288 字节 = 512KB)分块存储,上传/下载时内存里最多驻留一个块——小堆部署(本文 256–512MB)不要把它调大。存储后端三选一(default-store 指向 stores 里某项的 id):
| 类型 | stores 示例 id | 说明 |
|---|---|---|
default-database | database(默认启用) | 文件进元数据库 blob,备份一个库即全备份 |
file-system | filesystem(默认关) | 落本地目录 directory,joex 与 restserver 需共享该路径 |
s3 | minio(默认关) | 对象存储(endpoint/bucket/access-key/secret-key) |
⚠️ 注意:reference.conf 原文强调 所有节点(restserver+joex)的 files 段必须完全一致;
chunk-size还影响库内 blob 的分块方式,建库后不要改。
5.8 全文检索(full-text-search.*,两服务各一份)
默认 enabled = false。开启需同时给 restserver 与 joex 配同一份 full-text-search 段,实测本部署用 Solr:
full-text-search {
enabled = true
backend = "solr"
solr.url = "http://docspell-solr:8983/solr/docspell"
}
💡 技术拓展:Docspell 全文检索引擎二选一——Solr(独立索引服务,功能完整、需额外内存/磁盘,官方 compose 示例同款)与 PostgreSQL(复用元数据库的 tsvector,省一个服务,
pg-query-parser默认websearch_to_tsquery,适合轻量部署)。两者都支持后启用;启用后老数据需 reIndex 补索引。
Solr 子参数:commit-within=1000(毫秒,控制索引可见延迟)、def-type="lucene"、q-op="OR"(多词默认关系)。实测本部署检索闭环(content: 查询命中):
$ curl -s -H "X-Docspell-Auth: <token>" --get --data-urlencode "q=content:consume2" http://localhost:8137/api/v1/sec/item/search
groups: [('Results', ['consume2.txt'])]
$ curl -s -H "X-Docspell-Auth: <token>" --get --data-urlencode "q=content:\"cloud services\"" http://localhost:8137/api/v1/sec/item/search
groups: [('Results', ['import-note.txt'])]
joex 侧另有 full-text-search.migration.index-all-chunk = 10(重建索引每批装载的附件数),低内存部署保持默认或调小。
本栈实测连踩两坑(完整过程见 7.2):① Solr URL 用的主机名 docspell-solr 需在编排网络里显式设 hostname,否则 UnknownHostException;② 首次启动时 Solr 不可达会静默跳过 schema 自动初始化,修复 DNS 后必须重启两 JVM 服务让 Docspell 检测空 core 并建 38 字段 schema,仅 reIndex 不够。
5.9 joex 调度与家务(scheduler / house-keeping)
scheduler.pool-size = 1:单 joex 一次处理 1 个作业;堆 512MB 的小机器不建议调大(OCR/NLP 作业内存峰值高)。counting-scheme = "4,1":每 4 个高优先级作业插 1 个低优先级,防饿死。集成端点/目录监视提交的作业默认 low prio(integration-endpoint.priority="low"),手动上传是 high。retries = 2+retry-delay = "1 minute"(指数递增):失败作业变 stuck 后自动重试,两次仍败进 failed。7.1 的 NLP OOM 正是靠这个机制形成"崩溃-重试-再崩溃"循环的。wakeup-period = "30 minutes":队列空时自唤醒兜底。house-keeping.schedule = "Sun *-*-* 00:00:00 UTC":周日 UTC 零点跑清理五件套——cleanup-invites/cleanup-remember-me/cleanup-jobs(30 days,delete-batch=100)/cleanup-downloads(14 days)/integrity-check(按校验和全量核对文件,大库注意其 IO 开销)。
5.10 joex 文本抽取与 OCR(extraction.*)
处理流水线的资源阀门:
| 键 | 默认 | 调参要点 |
|---|---|---|
pdf.min-text-len | 500 | PDF 原生文本 ≥500 字符就不跑 OCR;设 0 = 永远 OCR |
preview.dpi | 32 | 预览图约 200×300px;改后要重生成预览(有专用接口) |
ocr.max-image-size | 14000000 | 像素数上限(约 1400 万像素),超限跳过——OCR 整图进内存,小堆必守 |
ocr.page-range.begin | 10 | 只 OCR 前 10 页;-1 全量 |
ocr.ghostscript/unpaper/tesseract | gs/unpaper/tesseract,timeout 5 minutes | 外部命令路径与超时;joex 官方发行件已捆绑全套工具 |
OCR 链路固定为 ghostscript 转灰度图 → unpaper 去噪 → tesseract 识字,三段命令均可改 program/args/timeout。
5.11 joex 文本分析与 NLP(text-analysis.*)——本批最大坑源
max-length = 5000:超过 5000 字符的文本截断后再分析(约两页),直接决定 NLP/分类器的内存占用,官方注释称 80% 文档短于此值。
nlp.mode 四态:
💡 技术拓展:NLP 实体识别模式四选一——full(完整 Stanford CoreNLP 管线,效果最好,官方注释要求 joex
-Xmx1400M起步)、basic(仅 NER 标注器,约 500MB 堆)、regexonly/regexner(不依赖语言模型,把通讯录条目转正则匹配,通讯录大时也吃内存)、disabled(完全关闭 Stanford NLP,只剩分类器结果)。默认full。
实测本部署 -Xmx512m 跑默认 full 直接 OOM 崩溃循环(完整实录见 7.1),最终选 disabled:
environment:
DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE: 'disabled'
配套键:clear-interval = "15 minutes"(CoreNLP 模型缓存闲置多久后释放,内存充裕可设 0 提速)、max-due-date-years = 10(到期日建议的远期上限)、regex-ner.max-entries = 1000(通讯录转正则的条目上限)。classification.enabled = true + item-count = 600:自动标签分类器全局开关与训练样本上限——"这个值和 text-analysis.max-length 一起决定内存需求,弱硬件先调这两个"(reference.conf 原注)。
5.12 joex 格式转换(convert.*)
上传的 Word/图片/HTML 等都要转成 PDF 归档。html-converter 二选一:
💡 技术拓展:HTML→PDF 转换器两选一——wkhtmltopdf(默认,Qt WebKit 渲染,
wkhtmlpdf段配置)与 weasyprint(Python 现代 CSS 渲染,weasyprint段配置)。本部署实测选weasyprint(环境变量DOCSPELL_JOEX_CONVERT_HTML__CONVERTER='weasyprint')。
实测回显(作业日志,text/plain 转 PDF):
Running external command: weasyprint --encoding UTF-8 - /tmp/docspell-weasyprint/docspell-weasyprint17565070835853757656/out.pdf
PDF conversion failed: Cannot run program "weasyprint" (in directory "/tmp/docspell-weasyprint/docspell-weasyprint17565070835853757656"): Failed to exec spawn helper: pid: 394, exit code: 1, error: 0 (none)
随后作业日志给出 Go without PDF file——转换失败不致命,文本抽取照常完成、条目正常入库(该条目后续 confirm/检索全部通过)。这是 Docspell 转换链的容错设计:ocrmypdf 段注释同样写明"失败不 fatal,继续用原 PDF 抽文本"。其余转换工具段:tesseract(图片→PDF 一步带 OCR,含日语竖排 jpn_vert 定制 arg-mappings)、unoconv(Office 全家桶转 PDF,官方建议常驻 unoconv -l 监听提速)、decrypt-pdf.enabled = true + passwords = [](去 PDF 保护,加密文件需在此或集体级配密码才能读)。converted-filename-part = "converted" 决定转换产物命名(实测附件名 import-note.converted.pdf)。
5.13 跨服务一致性约束清单
| 约束 | 键 | 违反后果 |
|---|---|---|
| 同库 | server.backend.jdbc.* = joex.jdbc.* | 数据与作业分裂 |
| 同文件存储 | 两处 files.* 完全一致 | joex 读不到 restserver 写的文件 |
| 同检索配置 | 两处 full-text-search.* 一致 | 索引写入与查询引擎不一致 |
| 同 secret | 多 restserver auth.server-secret | 跨节点 token 互不认 |
| internal-url 可达 | server.internal-url | joex 回调失败、队列卡死 |
六、生产配置模板
适用场景:16GB 内存 / NVMe SSD / 单机小团队(<10 用户),JVM 堆限 256–512MB,PostgreSQL + Solr 全功能栈。restserver 侧为本文 3.2 实测通过的文件原样:
# docspell-server.conf —— 启动第一个参数传入(见 3.2)
docspell.server {
app-name = "Docspell"
base-url = "https://docs.example.com" # 对外地址,生产改 TLS 域名
internal-url = "http://docspell-restserver:7880"
bind.address = "0.0.0.0"
auth {
server-secret = "b64:<64位随机>" # 生产用强随机,多实例共享
session-valid = "5 minutes"
}
admin-endpoint.secret = "<强随机>"
integration-endpoint {
enabled = true
http-header {
enabled = true
header-name = "Docspell-Integration"
header-value = "<强随机>"
}
}
full-text-search {
enabled = true
backend = "solr"
solr.url = "http://docspell-solr:8983/solr/docspell"
}
backend {
jdbc {
url = "jdbc:postgresql://db:5432/docspell"
user = "docspell"
password = "<db密码>"
}
signup.mode = "invite" # 生产建议邀请制
new-invite-password = "<邀请口令>"
addons.enabled = false
}
}
joex 侧等价模板(键值与本文实测环境变量形态一一对应):
# docspell-joex.conf
docspell.joex {
app-id = "joex1"
base-url = "http://docspell-joex:7878"
bind.address = "0.0.0.0"
jdbc {
url = "jdbc:postgresql://db:5432/docspell"
user = "docspell"
password = "<db密码>"
}
full-text-search {
enabled = true
backend = "solr"
solr.url = "http://docspell-solr:8983/solr/docspell"
}
text-analysis.nlp.mode = "disabled" # 堆 <1400M 必关,见 7.1
convert.html-converter = "weasyprint"
addons {
enabled = true
executor-config.runner = "trivial"
}
}
JVM 堆不在 HOCON 里,走 JAVA_OPTS(启动脚本读取):
JAVA_OPTS='-Xms256m -Xmx512m'
💡 说明:本文全程把两服务堆限约束在 256–512MB,因此凡默认值按"大内存"设计的键(
nlp.mode=full、OCR 全页、max-item-page-size拉满)都要按 5.10/5.11 的阀门收紧;若日后把堆放宽到 1.5GB+,可回开nlp.mode=full获得实体建议能力。
七、常见问题
7.1 joex 反复重启:NLP full 模式 OOM
现象:joex 进程反复 Restarting,日志尾部:
docspell.analysis.nlp.StanfordNerAnnotator.makePipeline:39 - Creating german Stanford NLP NER classifier...
Exception in thread "io-compute-blocker-3" java.lang.OutOfMemoryError: Java heap space
栈顶 edu.stanford.nlp.ie.crf.CRFClassifier.loadClassifier(CRFClassifier.java:2601)
根因:text-analysis.nlp.mode 默认 full,官方注释要求 -Xmx1400M 起步;本部署 512MB 堆加载德语 NER 模型即溢出,进程崩溃后作业经 scheduler.retries 重试,形成循环。修复(不加大堆的官方取舍):nlp.mode=disabled(环境变量 DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE='disabled')。验证:重建后不再重启,积压作业重跑日志 Classification is disabled. Check config or settings. → Text-Analysis finished in 108 ms → Job execution successful。代价:失去 NLP 实体建议,regex 与分类器路径不受影响。
7.2 全文检索 500:DNS 缺失与 schema 未初始化
两段式排障。其一,content: 查询 500 空体,restserver 日志 java.net.UnknownHostException: docspell-solr: Temporary failure in name resolution——Solr 主机名别名未在编排网络显式声明(hostname: docspell-solr),而 solr.url 指向它。修复加 hostname 并重建该服务。其二,DNS 修好仍 500,日志变为 400 Bad Request for request POST http://docspell-solr:8983/solr/docspell/query——core 用默认示例 schema 建立,无 docspell 字段;官方机制"检测到空 core 自动建 schema"在首次启动时因 Solr 不可达被静默跳过。修复:DNS 就绪后重启两 JVM 服务,Docspell 经 Schema API 自动建 38 字段,再 POST /sec/fts/reIndex(集体级)或 admin reIndexAll(全局)。验证见 5.8 实测命中回显。
7.3 挂了配置文件后服务失联/数据"清空"
即 3.2 的核心发现:传配置文件后环境变量层整体失效,bind.address 回落 localhost、jdbc 回落 H2 空库,表象就是"入口打不开"或"数据没了"。数据没丢——撤掉配置文件重启即回 PostgreSQL 原库。解法:配置文件自包含全部必需键(对照 3.2 模板清单)。
7.4 改了挂载的配置文件内容但参数没变
编排工具只跟踪挂载定义变化,bind 文件内容变化不触发服务重建;需强制重建该服务(up -d --force-recreate <服务>,完整命令见《Docspell Docker Compose 部署指南》运维章)。改完可用行为反证(3.5 表)确认新值生效。
7.5 环境变量写了没生效
三连查:① 映射规则是否写对(-→__、.→_,3.3);② 是否传了配置文件把环境变量层顶掉了(3.2);③ 服务是否真正重建(7.4)。变量名错拼不会报错,只会静默用默认值。
7.6 重启后所有人都被登出
auth.server-secret 留空(默认)则每次启动随机生成签名密钥,旧 token 全部作废。生产必显式设置固定强密钥(5.2)。
八、参考链接
- Docspell 官方配置文档(configure 章节):https://docspell.org/docs/configure/ (查询日期 2026-11-01)
- 内置默认参数:
reference.conf(restserver 534 行 / joex 940 行,本文 2.2 提取方法) - Typesafe Config(HOCON 规范与三层合并语义):https://github.com/lightbend/config
- 《Docspell 介绍与选型指南》——组件定位与架构总览
- 《Docspell Docker Compose 部署指南》——环境搭建、环境变量表与运维命令
- 《Docspell 0.43.0 API 使用详解》——全端点族 curl 实测与响应回显