Docspell Docker Compose 部署指南
Docspell 0.43.0 多服务栈(restserver+joex+dsc+PostgreSQL+Solr)Docker Compose 部署全流程:实测回显、环境变量映射表、JVM 堆配置与两则排障实录
Docspell Docker Compose 部署指南
目标:在 Ubuntu 26.04 服务器上用 Docker Compose 从零部署 Docspell 0.43.0 完整功能栈(restserver + joex + dsc 目录监视 + PostgreSQL + Solr 全文检索),完成注册、上传、检索、目录摄取的全链路验证,并沉淀本批部署实际踩过的坑与排障方案。
适用版本:Docspell v0.43.0(2025-03-15 构建)。文中所有命令与回显均为 2026-11-01 实测。
一、环境信息
本指南基于以下真实服务器环境实测,所有命令与输出均逐条验证:
| 项目 | 值 | 验证命令 |
|---|---|---|
| 操作系统 | Ubuntu 26.04.1 LTS | cat /etc/os-release |
| 内核 | 7.0.0-38-generic | uname -r |
| CPU / 内存 | 6 核 / 15 GiB | nproc / free -h |
| Docker | 29.9.2, build a82e528 | docker --version |
| Docker Compose | v5.6.1 | docker compose version |
| Docspell | v0.43.0(builtAtString 2025-03-15 16:46:56.280+0100,gitCommit 6b4e03ac3a2ac013783ec42245c01b7c0ff0dca9) | curl http://localhost:8137/api/info/version |
Docker 引擎实测回显:
$ docker --version
Docker version 29.9.2, build a82e528
$ docker compose version
Docker Compose version v5.6.1
Docspell 栈对外仅暴露 restserver 的 Web/API 端口(本例宿主映射 8137,组件内部端口 7880),PostgreSQL、Solr、joex 均不对外发布端口,仅通过 Compose 内部网络 docspell-net(固定网段 10.89.59.0/24)互通。
二、镜像选型说明
Docspell 官方镜像发布在 GitHub 容器组织的 docspell 名下(ghcr.io),CLI 工具 dsc 同时发布在 Docker Hub(来源:docspell.org/docs/install/docker 页 "Docker Images" 一节,查询日期 2026-11-01):
| 镜像 | 作用 | 实测大小 | 说明 |
|---|---|---|---|
| ghcr.io/docspell/restserver:v0.43.0 | HTTP 服务(Web UI + REST API) | 1.43 GB | 内嵌 OpenJDK 21(java -version 实测 21.0.11) |
| ghcr.io/docspell/joex:v0.43.0 | 作业执行器 | 6.13 GB | 已捆绑全部外部处理工具(ocrmypdf、unoconv、ghostscript、tesseract 等),无需另装 |
| docspell/dsc:latest | 命令行客户端(目录监视 watch) | 34.8 MB | 镜像不定义 CMD/ENTRYPOINT,必须在 compose 里显式给出 command |
| postgres:18.6 | 元数据与文件存储 | 650 MB | 官方 compose 示例同款 |
| solr:10 | 全文检索引擎 | 1.23 GB | 官方 compose 示例同款 |
官方镜像标签策略(来源:同上 Docker 页,查询日期 2026-11-01):
latest恒指向最新 release;每个 release 同时打对应版本号标签(如v0.43.0);- 另有
nightly标签由 master 分支构建,官方明确提示 "should be used with care, because things might break in between"。
选型结论:生产部署应钉死版本号标签(本例 v0.43.0),不要用 latest/nightly——Docspell 不支持降级(官方 Docker 页 Upgrading 一节原文 "Since downgrading is not supported"),latest 漂移会直接撞上不可回退的升级。
💡 技术拓展:Docspell 官方提供多种部署形态,不止 Compose 一种——
形态 特点 适用场景 docker run 手工组网 官方最小示例用 H2 内嵌库,无全文检索 快速尝鲜,不适合生产 Docker Compose 一条命令拉起 PostgreSQL + Solr 完整栈 单服务器生产(本文方案) Helm Chart 官方仓库 charts/docspell,StatefulSet 形态 Kubernetes 集群 手动安装(二进制 + systemd) 自行管理 JDK/数据库/外部工具 无 Docker 的环境 本文采用官方推荐的 Docker Compose 形态,因其自带 FTS 与 PostgreSQL 且仅一条启动命令。
三、前置条件
部署前先建好目录骨架。compose 文件用相对路径挂载数据与摄取目录,本例统一放在 /opt/docspell:
$ mkdir -p /opt/docspell/{data,import/admin}
$ cd /opt/docspell
两个必须注意的前置点(均为本批实测踩坑,详见第十一章):
import/admin/子目录:dsc watch 摄取目录时,监视根目录下必须存在以 collective 名命名的子目录(本例注册 collective 为admin),否则文件不会被摄取,日志报Error finding collective…Make sure there is a subdirectory with the name of the collective。- JVM 堆内存:restserver 与 joex 都是 Scala/JVM 进程,官方 joex 默认配置在启用 NLP full 模式时要求
-Xmx1400M以上;小内存服务器应通过JAVA_OPTS限堆(本例-Xms256m -Xmx512m)并关闭 NLP 模式,见第七章。
四、部署步骤
4.1 准备 compose 文件
将下列内容保存为 /opt/docspell/docker-compose.yml。这是本批实测终稿(在官方 docker-compose 示例基础上:钉死 v0.43.0 版本、注入 JAVA_OPTS 堆限制、joex 关闭 NLP、solr 补 hostname、命名卷改 bind 挂载、固定网段):
name: docspell-0.43.0-stack
services:
restserver:
image: ghcr.io/docspell/restserver:v0.43.0
hostname: docspell-restserver
container_name: docspell-0.43.0-restserver
restart: unless-stopped
ports:
- "8137:7880"
environment:
TZ: 'Asia/Shanghai'
JAVA_OPTS: '-Xms256m -Xmx512m'
DOCSPELL_SERVER_INTERNAL__URL: 'http://docspell-restserver:7880'
DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET: 'q1w2e3r4'
DOCSPELL_SERVER_AUTH_SERVER__SECRET: 'q1w2e3r4'
DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD: 'q1w2e3r4'
DOCSPELL_SERVER_BACKEND_JDBC_URL: 'jdbc:postgresql://db:5432/docspell'
DOCSPELL_SERVER_BACKEND_JDBC_USER: 'docspell'
DOCSPELL_SERVER_BIND_ADDRESS: '0.0.0.0'
DOCSPELL_SERVER_FULL__TEXT__SEARCH_ENABLED: 'true'
DOCSPELL_SERVER_FULL__TEXT__SEARCH_SOLR_URL: 'http://docspell-solr:8983/solr/docspell'
DOCSPELL_SERVER_INTEGRATION__ENDPOINT_ENABLED: 'true'
DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_ENABLED: 'true'
DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE: 'q1w2e3r4'
DOCSPELL_SERVER_BACKEND_SIGNUP_MODE: 'open'
DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD: ''
DOCSPELL_SERVER_BACKEND_ADDONS_ENABLED: 'false'
networks:
- docspell-net
depends_on:
- db
joex:
image: ghcr.io/docspell/joex:v0.43.0
hostname: docspell-joex
container_name: docspell-0.43.0-joex
restart: unless-stopped
environment:
TZ: 'Asia/Shanghai'
JAVA_OPTS: '-Xms256m -Xmx512m'
DOCSPELL_JOEX_APP__ID: 'joex1'
DOCSPELL_JOEX_PERIODIC__SCHEDULER_NAME: 'joex1'
DOCSPELL_JOEX_SCHEDULER_NAME: 'joex1'
DOCSPELL_JOEX_BASE__URL: 'http://docspell-joex:7878'
DOCSPELL_JOEX_BIND_ADDRESS: '0.0.0.0'
DOCSPELL_JOEX_FULL__TEXT__SEARCH_ENABLED: 'true'
DOCSPELL_JOEX_FULL__TEXT__SEARCH_SOLR_URL: 'http://docspell-solr:8983/solr/docspell'
DOCSPELL_JOEX_JDBC_PASSWORD: 'q1w2e3r4'
DOCSPELL_JOEX_JDBC_URL: 'jdbc:postgresql://db:5432/docspell'
DOCSPELL_JOEX_JDBC_USER: 'docspell'
DOCSPELL_JOEX_ADDONS_EXECUTOR__CONFIG_RUNNER: 'docker,trivial'
DOCSPELL_JOEX_CONVERT_HTML__CONVERTER: 'weasyprint'
DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE: 'disabled'
networks:
- docspell-net
depends_on:
- db
consumedir:
image: docspell/dsc:latest
container_name: docspell-0.43.0-consumedir
command:
- dsc
- "-d"
- "http://docspell-restserver:7880"
- "watch"
- "--delete"
- "-ir"
- "--not-matches"
- "**/.*"
- "--header"
- "Docspell-Integration:q1w2e3r4"
- "/opt/docs"
restart: unless-stopped
volumes:
- ./import:/opt/docs
networks:
- docspell-net
depends_on:
- restserver
db:
image: postgres:18.6
container_name: docspell-0.43.0-db
restart: unless-stopped
volumes:
- ./data/postgres:/var/lib/postgresql
environment:
POSTGRES_USER: 'docspell'
POSTGRES_PASSWORD: 'q1w2e3r4'
POSTGRES_DB: 'docspell'
networks:
- docspell-net
solr:
image: solr:10
hostname: docspell-solr
container_name: docspell-0.43.0-solr
restart: unless-stopped
volumes:
- ./data/solr:/var/solr
environment:
SOLR_OPTS: "-Dsolr.modules=analysis-extras"
command:
- bash
- -c
- 'precreate-core docspell; exec solr start -f --user-managed'
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8983/solr/docspell/admin/ping"]
interval: 1m
timeout: 10s
retries: 2
start_period: 30s
networks:
- docspell-net
networks:
docspell-net:
driver: bridge
ipam:
config:
- subnet: 10.89.59.0/24
关键结构解读:
- 五个服务各司其职:
restserver(对外 Web/API,宿主 8137→内部 7880)、joex(异步作业:OCR/文本抽取/索引,内部 7878 不对外)、consumedir(dsc watch 摄取目录)、db(PostgreSQL,schema 首启自动建表)、solr(全文检索 core 名为docspell,由precreate-core预建)。 - 三处 hostname 是服务间寻址的关键:
DOCSPELL_SERVER_INTERNAL__URL、DOCSPELL_JOEX_BASE__URL、两个 FTS 的SOLR_URL都按 hostname 寻址,compose 网络 DNS 必须能解析docspell-restserver/docspell-joex/docspell-solr这三个名字(官方示例靠container_name恰好同名,本例容器名带版本前缀,故 solr 显式加hostname: docspell-solr——漏掉它就是第十一章问题 2)。 - integration 密钥两处必须一致:restserver 的
DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE与 consumedir 命令行--header Docspell-Integration:…是同一把钥匙(官方 compose 注释原文 "both values must match")。 DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE: 'disabled':512 MB 堆下的官方取舍,原因与代价见第七章与第十一章问题 1。- 演示口令统一
q1w2e3r4;生产环境务必替换三处 secret(ADMIN__ENDPOINT_SECRET、AUTH_SERVER__SECRET、integration header 值)。
4.2 拉取镜像
$ docker compose pull
Image postgres:18.6 Pulling
Image ghcr.io/docspell/restserver:v0.43.0 Pulling
Image ghcr.io/docspell/joex:v0.43.0 Pulling
Image docspell/dsc:latest Pulling
Image solr:10 Pulling
Image postgres:18.6 Pulled
Image ghcr.io/docspell/joex:v0.43.0 Pulled
Image ghcr.io/docspell/restserver:v0.43.0 Pulled
Image docspell/dsc:latest Pulled
…(层下载进度,输出节选(实测首尾))
Image solr:10 Pulled
五个镜像合计约 9.5 GB(见第二章实测大小表),其中 joex 一个镜像就占 6.13 GB——因为它捆绑了 LibreOffice/OCR 全链路工具,首次拉取耗时最长,属正常现象。
4.3 启动
$ docker compose up -d
Container docspell-0.43.0-restserver Running
Container docspell-0.43.0-joex Running
Container docspell-0.43.0-consumedir Running
Container docspell-0.43.0-db Running
Container docspell-0.43.0-solr Recreate
Container docspell-0.43.0-solr Recreated
Container docspell-0.43.0-solr Starting
Container docspell-0.43.0-solr Started
(上为配置变更后一次真实 up -d 回显:未变的服务显示 Running,compose 检测到 solr 配置变化自动 Recreate——这正是 up -d 幂等增量生效的行为。)
4.4 验证服务状态
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
docspell-0.43.0-consumedir docspell/dsc:latest "dsc -d http://docsp…" consumedir 44 minutes ago Up 21 minutes
docspell-0.43.0-db postgres:18.6 "docker-entrypoint.s…" db 44 minutes ago Up 44 minutes 5432/tcp
docspell-0.43.0-joex ghcr.io/docspell/joex:v0.43.0 "/opt/joex-entrypoin…" joex 28 minutes ago Up 10 minutes 7878/tcp
docspell-0.43.0-restserver ghcr.io/docspell/restserver:v0.43.0 "/opt/docspell-rests…" restserver 44 minutes ago Up 10 minutes 0.0.0.0:8137->7880/tcp, [::]:8137->7880/tcp
docspell-0.43.0-solr solr:10 "docker-entrypoint.s…" solr 14 minutes ago Up 14 minutes (healthy) 8983/tcp
五个服务全部 Up,solr 带 healthcheck 状态 (healthy),仅 restserver 对外发布端口。端口监听复核:
$ ss -ltn | grep 8137
LISTEN 0 4096 0.0.0.0:8137 0.0.0.0:*
LISTEN 0 4096 [::]:8137 [::]:*
版本端点复核(注意:版本端点不在 /api/v1 前缀下,实测路径为 /api/info/version):
$ 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"}
Web UI 入口复核:根路径 / 返回 404,应用挂在 /app 下:
$ curl -s -I http://localhost:8137/ | head -1
HTTP/1.1 404 Not Found
$ curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8137/app
200
浏览器访问 http://<服务器IP>:8137/app 即见登录页。
4.5 注册首个账户
compose 环境变量 DOCSPELL_SERVER_BACKEND_SIGNUP_MODE: 'open' 允许自助注册。官方文档建议注册时 collective 名与用户名取同一个(来源:docspell.org/docs/install/docker 页,查询日期 2026-11-01),后续登录只用这一个名字。用 API 注册(演示账号 admin / q1w2e3r4):
$ curl -s -X POST http://localhost:8137/api/v1/open/signup/register \
-H "Content-Type: application/json" \
-d '{"collectiveName": "admin", "login": "admin", "password": "q1w2e3r4"}'
{"success":true,"message":"Signup successful"}
登录取 token(后续调用都带 X-Docspell-Auth 头;token 有效期 validMs: 300000,即 5 分钟):
$ 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":"1793535919263-MS9hZG1pbi9IRDZaNkJEUFEzai1BWWJpbVRWMXlXRi1tSlVidzhVZWhEdy1QS1RXeWJlZ1RvNS9hZG1pbg==-false-$2a$10$QhJTQpsU9gXdRtSdVlcRIe-pesNo6DW0D0TbNlMIaMOYP24xLA=","validMs":300000,"requireSecondFactor":false}
$ TOKEN=<上面返回的 token 值>
注册完成后建议把 signup mode 改为 closed 或 invite(官方 compose 注释原话,见第十一章问题 6)。
4.6 上传与检索验证
上传一份文本文件(meta 与 file 两个 multipart 部分):
$ curl -s -X POST http://localhost:8137/api/v1/sec/upload/item \
-H "X-Docspell-Auth: $TOKEN" \
-F 'meta={"multiple": true};type=application/json' \
-F "file=@import-note.txt"
{"success":true,"message":"Files submitted."}
上传是异步处理:文件先入队,由 joex 完成格式转换/文本抽取/索引。稍候按条件检索(结构化查询,q= 为查询表达式):
$ curl -s -H "X-Docspell-Auth: $TOKEN" "http://localhost:8137/api/v1/sec/item/search?q=&withDetails=true"
{
"groups": [
{
"name": "2026-11",
"items": [
{
"id": "DRBnvpvRfGV-oqdwHc1Jjpi-CW9MzZ3DeN2-Nby6RLJ5M59",
"name": "import-note.txt",
"state": "created",
"source": "webapp",
"direction": "incoming",
"attachments": [
{
"id": "7D9MRpPC1Nn-ukzRVAmb5bk-UZ6EkwB9262-vpDbdQVwJ3v",
"position": 0,
"name": "import-note.converted.pdf",
"pageCount": 1
}
],
"tags": []
…(其余空字段略,输出节选(实测首尾))
}
]
}
],
"limit": 200,
"offset": 0,
"limitCapped": false
}
条目已生成(state: created),且 joex 已把上传的 txt 自动转成了 import-note.converted.pdf 附件——这就是处理流水线跑通的标志。
Solr 全文检索验证(FTS 查询必须用 content: 字段前缀,裸词会被查询解析器拒绝):
$ 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'])]
4.7 目录监视摄取验证
向 import/admin/(collective 名子目录)放入新文件,dsc watch 自动经 integration 端点上传并删除源文件(实测回显):
$ echo 'consume3 watch test' > import/admin/consume3.txt
$ ls -l import/admin/
total 8
-rw-r--r-- 1 root root 73 Nov 1 20:44 consume-note.txt
-rw-r--r-- 1 root root 20 Nov 1 21:21 consume3.txt
$ sleep 20; ls -l import/admin/
total 4
-rw-r--r-- 1 root root 73 Nov 1 20:44 consume-note.txt
$ docker compose logs --tail=3 consumedir
docspell-0.43.0-consumedir | Sending request …
docspell-0.43.0-consumedir | Deleted file
docspell-0.43.0-consumedir | Server: Files submitted.
新文件 consume3.txt 被摄取并因 --delete 删除;目录里更早存在的 consume-note.txt 纹丝不动——印证 watch 只响应新文件事件、不补扫已存在文件(见第十一章问题 3)。检索验证全链路(watch → integration 上传 → joex 转换 → Solr 索引):
$ curl -s -H "X-Docspell-Auth: $TOKEN" --get --data-urlencode 'q=content:consume3' \
http://localhost:8137/api/v1/sec/item/search
{"groups":[{"name":"Results","items":[{"id":"X5aQqkdnXR5-2w7t5GZKR8F-jXzBaXX4YNu-aXs1BDo1uQ","name":"consume3.txt","state":"created","date":1793539278167,"dueDate":null,"source":"integration","direction":"incoming","corrOrg":null,"corrPerson":null,"concPerson":null,"concEquipment":null,"folder":null,"attachments":[],"tags":[],"customfields":[],"relatedItems":[],"notes":null,"highlighting":[{"name":"consume3.converted.pdf","lines":["**consume3**.converted.pdf","**consume3** watch test"]},{"name":"item","lines":["**consume3**.txt"]}]}]}],"limit":200,"offset":0,"limitCapped":false}
条目 source: integration 标记其来自目录摄取通道,highlighting 返回全文命中高亮。
五、数据持久化说明
本方案用 bind 挂载把全部状态落在 compose 同级的 data/ 目录(官方示例用命名卷,bind 化后备份与迁移更直观):
| 挂载 | 宿主机路径 | 内容 | 实测占用 |
|---|---|---|---|
| db → /var/lib/postgresql | ./data/postgres | PostgreSQL 数据目录(含 18 主版本子目录) | 68 MB |
| solr → /var/solr | ./data/solr | Solr core 数据与 schema | 1.7 MB |
| consumedir → /opt/docs | ./import | 摄取暂存目录(须含 collective 子目录) | — |
$ ls -l data/
total 8
drwxr-xr-x 3 root root 4096 Nov 1 20:23 postgres
drwxr-xr-x 4 8983 8983 4096 Nov 1 20:23 solr
$ du -sh data/*
68M data/postgres
1.7M data/solr
要点:Docspell 默认文件后端是 database——文档附件的二进制内容也存进 PostgreSQL(本例 data/postgres 68 MB 中即含 3 份测试文档),Solr 里只有索引。因此备份 PostgreSQL 就保住了全部档案,Solr 数据可随时用重建索引找回(见第十章)。
六、环境变量表
Docspell 用 Typesafe Config,环境变量名按固定规则映射到 HOCON 配置路径:前缀 DOCSPELL_,单个 _ 对应配置层级的 .,连续双 __ 对应配置键中的 -(连字符)。例:
| 环境变量 | 对应配置项 |
|---|---|
DOCSPELL_SERVER_BACKEND_JDBC_URL | docspell.server.backend.jdbc.url |
DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET | docspell.server.admin-endpoint.secret |
DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE | docspell.joex.text-analysis.nlp.mode |
DOCSPELL_SERVER_FULL__TEXT__SEARCH_SOLR_URL | docspell.server.full-text-search.solr-url |
本 compose 各服务变量一览(restserver,docspell.server.*):
| 环境变量 | 本例取值 | 含义 |
|---|---|---|
| JAVA_OPTS | -Xms256m -Xmx512m | JVM 堆限制(非 Docspell 配置,由镜像启动脚本读取) |
| DOCSPELL_SERVER_INTERNAL__URL | http://docspell-restserver:7880 | 内部自寻址 URL(通知 joex 等用) |
| DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET | q1w2e3r4 | 管理端点口令(Docspell-Admin-Secret 头) |
| DOCSPELL_SERVER_AUTH_SERVER__SECRET | q1w2e3r4 | 登录 token 签名密钥 |
| DOCSPELL_SERVER_BACKEND_JDBC_URL | jdbc:postgresql://db:5432/docspell | 元数据库连接 |
| DOCSPELL_SERVER_BACKEND_JDBC_USER / _PASSWORD | docspell / q1w2e3r4 | 数据库账号 |
| DOCSPELL_SERVER_BIND_ADDRESS | 0.0.0.0 | 监听全部接口(默认只监听 127.0.0.1,容器内必须放开) |
| DOCSPELL_SERVER_FULL__TEXT__SEARCH_ENABLED | true | 开启 Solr 全文检索 |
| DOCSPELL_SERVER_FULL__TEXT__SEARCH_SOLR_URL | http://docspell-solr:8983/solr/docspell | Solr core 地址 |
| DOCSPELL_SERVER_INTEGRATION__ENDPOINT_ENABLED | true | 开启 integration 上传端点 |
| DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_ENABLED | true | integration 用 HTTP 头认证 |
| DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE | q1w2e3r4 | integration 口令(须与 consumedir --header 一致) |
| DOCSPELL_SERVER_BACKEND_SIGNUP_MODE | open | 注册模式:open/invite/closed |
| DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD | '' | invite 模式的邀请口令(open 模式留空) |
| DOCSPELL_SERVER_BACKEND_ADDONS_ENABLED | false | 附加插件(addon)开关 |
joex 服务变量(docspell.joex.*):
| 环境变量 | 本例取值 | 含义 |
|---|---|---|
| JAVA_OPTS | -Xms256m -Xmx512m | JVM 堆限制 |
| DOCSPELL_JOEX_APP__ID / SCHEDULER_NAME / PERIODIC__SCHEDULER_NAME | joex1 | 执行器与调度器标识(多 joex 实例须互异) |
| DOCSPELL_JOEX_BASE__URL | http://docspell-joex:7878 | restserver 通知 joex 新作业用的地址 |
| DOCSPELL_JOEX_BIND_ADDRESS | 0.0.0.0 | 监听全部接口 |
| DOCSPELL_JOEX_FULL__TEXT__SEARCH_ENABLED / SOLR_URL | true / http://docspell-solr:8983/solr/docspell | joex 侧写索引 |
| DOCSPELL_JOEX_JDBC_URL / _USER / _PASSWORD | 同 restserver 指向同一库 | 作业队列与元数据 |
| DOCSPELL_JOEX_ADDONS_EXECUTOR__CONFIG_RUNNER | docker,trivial | addon 运行器 |
| DOCSPELL_JOEX_CONVERT_HTML__CONVERTER | weasyprint | HTML 转 PDF 用 weasyprint(官方 compose 同款) |
| DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE | disabled | 关闭 Stanford NLP(512 MB 堆取舍,见第七章) |
💡 技术拓展:joex 的 NLP 分析模式共四档(来源:joex reference.conf 0.43.0 注释,查询日期 2026-11-01)——
mode 能力 资源要求 full 加载 Stanford NLP 实体识别模型(德/英/法…) 官方注释要求 joex 堆 ≥1400 MB basic 仅部分轻量 NLP 功能 介于 full 与 regexonly 之间 regexonly 只用正则抽取日期/金额等,不加载 NLP 模型 低 disabled 完全不做文本分析 最低 本部署限堆 512 MB,选 disabled(full 模式实测 OOM 崩溃循环,见第十一章问题 1)。
七、配置参数说明(JVM 堆专节)
restserver/joex 的 JVM 参数通过镜像启动脚本读取环境变量 JAVA_OPTS 注入,compose 中位置即各服务的 environment: 段:
environment:
JAVA_OPTS: '-Xms256m -Xmx512m'
修改后必须实测确认生效,用进程命令行复核(两个 JVM 服务分别执行):
$ docker exec docspell-0.43.0-joex ps -ef | grep java
root 8 1 0 20:57 ? 00:00:08 java -Xms256m -Xmx512m -Dconfig.file=/opt/docspell-joex/bin/../conf/docspell-joex.conf -cp /opt/docspell-joex/lib/com.github.eikek.docspell-joex-0.43.0-classpath.jar docspell.joex.Main
$ docker exec docspell-0.43.0-restserver ps -ef | grep java
root 1 0 0 20:57 ? 00:00:08 java -Xms256m -Xmx512m -Dconfig.file=/opt/docspell-restserver/bin/../conf/docspell-server.conf -cp /opt/docspell-restserver/lib/com.github.eikek.docspell-restserver-0.43.0-classpath.jar docspell.restserver.Main
回显中 -Xms256m -Xmx512m 即实际生效堆参数。两点提示:
-Dconfig.file=…显示镜像默认加载自带的conf/docspell-{joex,server}.conf(其内容即官方 reference.conf 加环境变量覆盖),要换成自定义配置文件见第八章。- joex 若要用 NLP full 模式,
JAVA_OPTS至少-Xmx1400m(官方 reference.conf 注释原文),否则启动即 OOM——本批实测踩过,见第十一章问题 1。
功能配置项的完整清单与默认值不在本文展开(官方 docspell.server.* / docspell.joex.* 数百项),常用键的含义已在第六章列出。
八、配置文件挂载
环境变量适合少量关键项;参数多时官方推荐挂一个 HOCON 配置文件,把文件路径作为启动命令的参数传给镜像(来源:docspell.org/docs/install/docker 页,查询日期 2026-11-01)。实测流程:
$ cat > /tmp/ds-b2.conf <<'EOF'
docspell.server {
bind.address = "0.0.0.0"
internal-url = "http://docspell-restserver:7880"
backend.jdbc.url = "jdbc:postgresql://db:5432/docspell"
backend.jdbc.user = "docspell"
backend.jdbc.password = "q1w2e3r4"
auth.server-secret = "q1w2e3r4"
}
EOF
$ docker run -d --name docspell-0.43.0-conftest \
--network docspell-0430-stack_docspell-net \
-v /tmp/ds-b2.conf:/opt/docspell.conf \
-e JAVA_OPTS='-Xms256m -Xmx512m' \
ghcr.io/docspell/restserver:v0.43.0 /opt/docspell.conf
78050fa613c9ec995862b29a4ff3dd550b01f934a44928b635db349ba42ae4f3
等待启动后验证(容器 IP 由 docker inspect 取得,实测 10.89.59.7):
$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docspell-0.43.0-conftest
10.89.59.7
$ docker logs docspell-0.43.0-conftest 2>&1 | grep 'bound to address'
2026.11.01 13:23:00:0000 [io-comp...] [INFO ] org.http4s.ember.server.EmberServerBuilderCompanionPlatform - Ember-Server service bound to address: [::]:7880
$ curl -s http://10.89.59.7:7880/api/info/version
{"version":"0.43.0","builtAtMillis":1742053616280,"builtAtString":"2025-03-15 16:46:56.280+0100","gitCommit":"6b4e03ac3a2ac013783ec42245c01b7c0ff0dca9","gitVersion":"0.43.0"}
restserver 以挂载的 /opt/docspell.conf 为配置成功启动并响应 API。落到 compose 的写法(官方文档原话:给服务加 command: 传入配置文件路径):
restserver:
volumes:
- ./docspell.conf:/opt/docspell.conf
command:
- /opt/docspell.conf
注意:command 传配置文件后,环境变量这一整层会被完全忽略(服务回落 reference.conf 默认:内嵌 H2 库 + localhost 监听),并非"环境变量作覆盖层"——配置文件必须自包含全部必需键。实测过程与逐键验证见《Docspell 0.43.0 配置参数详解》3.2/3.4;本批终稿 compose 未用此形态(纯环境变量),此处为独立实测验证。
九、等价 docker run
官方 Docker 页的最小示例用 docker run 手工组网(H2 内嵌库、无 FTS),三条命令形态如下(来源:docspell.org/docs/install/docker 页 Examples 一节,查询日期 2026-11-01):
$ docker network create --subnet 10.4.3.0/24 dsnet
$ docker run -d --name ds-restserver --network dsnet --ip 10.4.3.3 \
-p 127.0.0.1:7880:7880 -v /tmp/testdb:/var/docspell \
-v /tmp/docspell.conf:/opt/docspell.conf docspell/restserver:latest /opt/docspell.conf
$ docker run -d --name ds-joex --network dsnet --ip 10.4.3.2 \
-v /tmp/testdb:/var/docspell -v /tmp/docspell.conf:/opt/docspell.conf \
docspell/joex:latest /opt/docspell.conf
本文 compose 栈中 restserver 服务的等价 docker run 实测(复用 compose 网络与 PostgreSQL):
$ docker run -d --name docspell-0.43.0-runtest \
--network docspell-0430-stack_docspell-net \
-e JAVA_OPTS='-Xms256m -Xmx512m' \
-e DOCSPELL_SERVER_BACKEND_JDBC_URL='jdbc:postgresql://db:5432/docspell' \
-e DOCSPELL_SERVER_BACKEND_JDBC_USER=docspell \
-e DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD=q1w2e3r4 \
-e DOCSPELL_SERVER_DEFAULTS_NEW_CONFIG_USER_PASSWORD=q1w2e3r4 \
-e DOCSPELL_SERVER_AUTH_ADMIN_API_TOKEN=q1w2e3r4 \
-e DOCSPELL_SERVER_APP_EXTERNAL_BASE_URL='http://localhost:8137' \
-e DOCSPELL_SERVER_BIND_ADDRESS=0.0.0.0 \
ghcr.io/docspell/restserver:v0.43.0
6361b0d3e4826b0319ddfa77595fcee19a603250442bbc96dbb85c8ca05e7c53
$ docker ps --filter name=runtest --format '{{.Names}} {{.Status}}'
docspell-0.43.0-runtest Up 32 seconds
$ docker logs docspell-0.43.0-runtest 2>&1 | tail -1
2026.11.01 13:10:40:0001 [io-comp...] [INFO ] org.http4s.ember.server.EmberServerBuilderCompanionPlatform - Ember-Server service bound to address: [::]:7880
$ curl -s http://10.89.59.7:7880/api/info/version
{"version":"0.43.0","builtAtMillis":1742053616280,"builtAtString":"2025-03-15 16:46:56.280+0100","gitCommit":"6b4e03ac3a2ac013783ec42245c01b7c0ff0dca9","gitVersion":"0.43.0"}
⚠️ 容器内没有 curl:docker exec docspell-0.43.0-runtest curl … 实测报 exec: "curl": executable file not found in $PATH(restserver/joex 镜像为精简 JRE 环境)。验证服务请用 docker logs 看绑定日志,或从宿主机直接 curl 容器 IP(如上面的 10.89.59.7)。
十、常用运维命令
状态与日志(实测回显):
$ docker compose logs --tail=2 restserver
docspell-0.43.0-restserver | 2026.11.01 21:08:03:0000 [io-comp...] [INFO ] org.http4s.server.middleware.Logger - HTTP/1.1 GET /app
docspell-0.43.0-restserver | 2026.11.01 21:08:03:0001 [io-comp...] [INFO ] org.http4s.server.middleware.Logger - HTTP/1.1 200 OK
restserver 默认把每条 HTTP 请求打进日志(如上面 GET /app → 200),排障时 docker compose logs -f <服务> 跟踪即可。
单服务重建(改配置后增量生效,up -d 只重建受影响服务,见 4.3 回显):
$ docker compose up -d solr
$ docker compose restart joex
数据库备份(pg_dump 直连 db 服务,实测):
$ docker exec docspell-0.43.0-db pg_dump -U docspell docspell > docspell-backup.sql
$ wc -l docspell-backup.sql
4377 docspell-backup.sql
$ grep -c 'CREATE TABLE' docspell-backup.sql
60
3 份测试文档的库导出 4377 行、60 张表。官方另提供 compose override 挂备份目录、以 docker-compose run 调 pg_dump 的备份流程(来源:docspell.org/docs/install/docker 页 Backups 一节,查询日期 2026-11-01),效果等价。
升级(官方 Docker 页 Upgrading 一节三步,注意先备份、不可降级):
$ docker compose down
$ docker compose pull
$ docker compose up --force-recreate -d
全文索引重建(Solr 数据丢失或换引擎后,用 API 触发,token 认证为 collective 级):
$ curl -s -X POST -H "X-Docspell-Auth: $TOKEN" http://localhost:8137/api/v1/sec/fts/reIndex
{"success":true,"message":"Full-text index will be re-created."}
十一、常见问题
问题 1:joex 反复 Restarting,日志报 OutOfMemoryError
现象:docker compose ps 显示 joex 状态 Restarting (1) 循环;日志尾部:
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
at edu.stanford.nlp.ie.crf.CRFClassifier.loadClassifier(CRFClassifier.java:2601)
定位:joex 默认 docspell.joex.text-analysis.nlp.mode = full,加载 Stanford NLP 德语 NER 分类器需要大堆(官方 reference.conf 注释要求 -Xmx1400M 起),本部署 -Xmx512m 直接堆溢出 → 进程崩溃 → 积压 process-item 作业重试 → 循环崩溃。
解决(官方取舍,不加大堆):给 joex 注入环境变量 DOCSPELL_JOEX_TEXT__ANALYSIS_NLP_MODE: 'disabled',docker compose up -d joex 重建。验证:不再重启,积压作业重跑成功,日志出现 Text-Analysis finished in 108 ms → Job execution successful。代价:失去 NLP 实体建议(correspondent/concerned 等 proposals 改由 regex 路径产出)。低堆环境也可退一档用 regexonly/basic。
问题 2:开了 Solr 但 content: 检索报 HTTP 500
现象一:GET /api/v1/sec/item/search?q=content:consume2 返回 HTTP 500 空体,restserver 日志:
java.net.UnknownHostException: docspell-solr: Temporary failure in name resolution
定位与解决:FTS URL 指向 http://docspell-solr:8983,但 compose 里 solr 服务只设了 container_name: docspell-0.43.0-solr,网络 DNS 解析不到 docspell-solr。给 solr 服务补 hostname: docspell-solr 并重建。
现象二:DNS 修好仍 500,日志变为 400 Bad Request for request POST http://docspell-solr:8983/solr/docspell/query(joex 侧 full-text-reindex 作业对 /update 同样 400)。
定位与解决:core 由 precreate-core docspell 以默认 _default 示例 schema 创建,没有 docspell 字段。官方文档说明 "If Docspell detects an empty core it will run a schema setup on start automatically",但首次启动时 solr 不可达(DNS 未修),自动 schema 初始化被静默跳过。修复 DNS 后必须 docker compose restart restserver joex 让 JVM 服务重新检测空 core 并自动建 schema(实测建出 38 个字段),再 POST /api/v1/sec/fts/reIndex 重建索引。验证:q=content:consume2 返回 200 且命中条目、高亮显示 **consume2**.converted.pdf。
结论:官方 compose 示例本身可用;踩坑点是①solr 主机名与 URL 不一致②solr 不可达时启动会静默跳过 schema 初始化,修复连通性后必须重启 JVM 服务而非仅 reIndex。
问题 3:往摄取目录放文件,Docspell 里没有动静
两个已实测确认的机制:
- 必须有 collective 名子目录:watch 根目录(
/opt/docs↔ 宿主./import)下要建import/<collective名>/(本例import/admin/),否则 consumedir 日志报Error finding collective…Make sure there is a subdirectory with the name of the collective,文件不摄取。 - watch 只响应新事件:已经存在于目录里的文件不会被补扫,放文件前先确认目录结构,再放入新文件;
--delete参数下摄取成功后源文件被删除,可据此确认闭环。
问题 4:docker exec <服务> curl 报 executable file not found
restserver/joex 镜像内无 curl(精简 JRE 环境,第九章实测)。验证一律走 docker logs 或宿主机 curl 容器 IP / 宿主映射端口。
问题 5:浏览器打开根路径 404
Docspell Web UI 挂在 /app(实测 GET / → 404,GET /app → 200)。访问地址用 http://<服务器IP>:8137/app。
问题 6:注册模式的安全收尾
compose 以 SIGNUP_MODE: open 便于首启注册,官方 compose 注释明确建议:建好账户后把 signup mode 改为 closed 或 invite(invite 需同时设置 DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD),否则任何人都能在公网实例上自助建档。
十二、参考链接
- Docspell 官方 Docker 安装文档:https://docspell.org/docs/install/docker (查询日期 2026-11-01)
- 官方 compose 示例仓库:https://github.com/docspell/docker (docker-compose/ 目录,含 Helm charts)
- Docspell 配置文档(环境变量映射与全量参数):https://docspell.org/docs/configure/
- Docspell 全文检索配置:https://docspell.org/docs/configure/fulltext-search
- 镜像仓库:https://github.com/orgs/docspell/packages 与 https://hub.docker.com/r/docspell/dsc
- 站内阅读:《Docspell 介绍与选型指南》