跳到主要内容

部署教程

Paperless-ngx 部署教程:中文 OCR、文档检索与备份

发布于

使用 VPS 部署 Paperless-ngx,介绍配置、访问方式、实际操作及数据备份。

开始前

合同已经扫描成 PDF,发票也下载到了电脑里,过几个月却想不起文件名。需要找某个订单号时,只能挨个打开。这类文件放进普通网盘后,目录是整齐了,纸面上的文字未必能直接搜索。

Paperless-ngx 可以接收 PDF 和图片,识别扫描件里的文字,再按标签、往来单位、文档类型和日期整理。你可以在浏览器里查找正文、查看原件和修改分类。它适合有持续归档需求的个人或小团队:收集报销凭证、合同、说明书、账单,把它们整理成一个能检索的资料库。

这套文档库使用 Paperless-ngx 3.2.1 和 Docker Compose,在 VPS 上运行。先保留本机访问入口,完成管理员初始化,再用 Caddy 接入 HTTPS。最后处理中文识别、共享权限和导出备份。全文搜索与 OCR 都能在自己的服务器上完成,无需为了这个用途接入大模型。

前往雨云选购 ↗

注册推荐码:KuZhuJi

先确认这些文件适不适合放上 VPS

Paperless-ngx 的重点是文档归档。它保留原始文件,提取文字,并根据文件和设置生成可供浏览的归档版本。标签与文档类型让一张发票既能按月份查,也能按供应商查,不必把同一个文件复制到几个目录里。

它不会替你确认合同条款,也不能保证 OCR 读出的金额、身份证号或账户号码准确。搜索帮助找到文件,最后使用其中的信息时仍要对照原件。照片歪斜、印章覆盖文字、低分辨率和手写内容都可能影响识别。

选择部署位置之前,要看这些资料是否适合保存到那台机器。普通 OCR 使用本地 Tesseract;但自托管不等于文件已经加密。Paperless-ngx 的功能说明明确提到,文件以明文形式存放在磁盘上。拿到服务器磁盘、备份或足够权限的管理员,可能读到这些材料。HTTPS 保护传输,无法代替服务器存储和账户权限管理。

个人说明书、公开资料和普通票据可以先作为入门样本。包含商业秘密、客户身份资料或其他敏感内容时,先确认保存位置、授权和备份方式是否合适;不适合上公网的资料,可以放在内网服务器,通过自己的安全访问通道使用。不要为了“能在手机打开”而直接公开整套文档库。

项目还提供可选 AI 和远程 OCR 功能,但这些有各自的配置与数据流向。本文只使用本地 OCR,不开启它们。

给 OCR 留资源,也给原件留空间

一个只有几位使用者、按天少量导入文件的文档库,可以从 2 核、4GB 内存的 VPS 考虑起。这是本文对小规模用途的建议,不是官方最低配置,也不是吞吐量承诺。批量识别几百页 PDF、同时运行其他应用时,要另外估算内存和处理时间。

OCR 往往在导入阶段集中使用 CPU 和内存,平时浏览文档则是另一种负载。选机器时不要只看“首页能打开”,还要考虑最常导入的文件页数、清晰度和图片尺寸。本文把后台任务和每个任务的线程数都从 1 开始,避免新手第一次导入就同时压上多个识别任务;确认资源余量后再调。

存储要容纳原件、归档文件、缩略图、数据库和导出副本。已有一批扫描件时,先统计其总容量,再给后续资料和临时导出留余量。磁盘快满时,继续导入和备份都可能失败。

选择现有 VPS 或购买新实例时,确认完整 Docker 支持、磁盘可扩展方式、流量及备份安排即可。例如雨云服务器可以作为选型入口,实际按当前机房、规格和价格比较。这里的任务会用到 OCR 算力和持久化存储,不要只因套餐便宜就选一个内存不足、还承载很多其他应用的实例。

准备 Linux 管理账号、可用的 Docker Engine 与 Compose 插件。没有安装时按Docker 官方安装文档操作;有面板或已有 Docker 的服务器,先确认版本和现有端口,不要重复安装一套。

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
docker --version
docker compose version

本文使用的管理账号需要有权限调用 Docker。该权限可以控制宿主机,应只给可信的管理员。Caddy 使用宿主机服务形式;如果你已有容器形式的反向代理,需要改为容器网络互通,不能直接照搬后面的回环地址。

先建立目录和两份私有配置

这里选择 PostgreSQL 数据库与 Valkey 消息代理。Valkey 是兼容 Redis 协议的服务,所以 Paperless 的连接设置仍叫 `PAPERLESS_REDIS`。这与 3.2.1 发布标签中的官方 Compose 文件保持一致。

先在普通管理账号的家目录建立项目:

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
mkdir -p ~/paperless/consume ~/paperless/export
cd ~/paperless
umask 077
printf 'PAPERLESS_DB_PASSWORD=%s\n' "$(openssl rand -hex 32)" > .env
printf 'PAPERLESS_SECRET_KEY=%s\n' "$(openssl rand -hex 64)" > docker-compose.env
printf 'USERMAP_UID=%s\nUSERMAP_GID=%s\n' "$(id -u)" "$(id -g)" >> docker-compose.env

这几行分别生成数据库密码、应用签名密钥,并记录宿主机管理账号的 UID 和 GID。它们应在首次安装时执行,后续更新不要再次覆盖这两份文件。更换数据库环境变量不会自动修改已初始化数据库里的密码;随手重生成密钥也可能影响已有会话。

在 `docker-compose.env` 后面补上以下设置,把域名换成自己的地址:

ini;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
PAPERLESS_URL=https://docs.example.com
PAPERLESS_TIME_ZONE=Asia/Shanghai
PAPERLESS_OCR_LANGUAGES=chi-sim
PAPERLESS_OCR_LANGUAGE=chi_sim+eng
PAPERLESS_OCR_MODE=auto
PAPERLESS_TASK_WORKERS=1
PAPERLESS_THREADS_PER_WORKER=1

中文配置的两处写法不同:`PAPERLESS_OCR_LANGUAGES` 是安装额外语言包,简体中文写 `chi-sim`;`PAPERLESS_OCR_LANGUAGE` 是 Tesseract 实际使用的语言代码,写 `chi_sim`。英文与中文一起识别时用 `chi_sim+eng`,多种语言也会增加处理开销。

本文使用容器默认的启动与用户映射方式,让启动脚本安装中文语言包。不要额外给这个容器加 `user: 1000:1000` 来强制无 root 启动;官方说明,启动时安装额外语言包与这种运行方式不兼容。需要严格无 root 的环境,应先准备带所需语言包的镜像,不能只改一个字段。

`PAPERLESS_URL` 要包含协议,不带结尾斜杠,也不要写成 `https://example.com/paperless`。它会帮助配置允许的主机与请求来源,适合独立子域名。详细含义见Paperless-ngx 配置文档。

写 Compose,数据库端口不要向公网开放

创建 `docker-compose.yml`:

yaml;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
services:
  broker:
    image: docker.io/valkey/valkey:9-alpine
    restart: unless-stopped
    volumes:
      - redisdata:/data

  db:
    image: docker.io/library/postgres:18
    restart: unless-stopped
    volumes:
      - pgdata:/var/lib/postgresql
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: ${PAPERLESS_DB_PASSWORD:?set PAPERLESS_DB_PASSWORD}

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:3.2.1
    restart: unless-stopped
    depends_on:
      - db
      - broker
    ports:
      - "127.0.0.1:8000:8000"
    volumes:
      - data:/usr/src/paperless/data
      - media:/usr/src/paperless/media
      - ./export:/usr/src/paperless/export
      - ./consume:/usr/src/paperless/consume
    env_file: docker-compose.env
    environment:
      PAPERLESS_REDIS: redis://broker:6379
      PAPERLESS_DBHOST: db
      PAPERLESS_DBENGINE: postgresql
      PAPERLESS_DBNAME: paperless
      PAPERLESS_DBUSER: paperless
      PAPERLESS_DBPASS: ${PAPERLESS_DB_PASSWORD:?set PAPERLESS_DB_PASSWORD}

volumes:
  data:
  media:
  pgdata:
  redisdata:

数据库与 Web 服务从同一个 Compose 变量取得密码,避免两边写成不同值。`db` 和 `broker` 只在项目网络里提供服务,没有给宿主机发布 5432 或 6379 端口。

这里用的是 PostgreSQL 18,数据卷挂到 `/var/lib/postgresql`。这是该版本官方 Compose 文件采用的路径,不要把旧教程中的 `/var/lib/postgresql/data` 不加核对地搬过来。已经有 PostgreSQL 旧版本数据卷时,也不能只把镜像改成 18;数据库大版本迁移需要单独安排。

`data` 保存应用数据,`media` 保存文档等媒体,`pgdata` 保存数据库。`consume` 是投递文件的入口,`export` 是导出目录。`consume` 用于接收待处理文件,不能当成归档原件目录。

Web 端口只绑定 `127.0.0.1`。所以启动后,即使在云防火墙开放 8000,外部也不能直接访问这个端口。先通过本机入口初始化,随后由反向代理提供正式访问。

检查配置并拉取镜像:

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 webserver

`config --quiet` 会检查 Compose 配置,避免把包含展开密码的完整配置输出到终端记录里。拉取镜像后,首次启动还会准备数据库、运行迁移和安装额外语言包,需要根据日志判断完成状态;只有容器列表出现名字,不代表应用已经能处理文档。

首次启动卡住时,保留日志里的错误。镜像拉取、容器启动后下载中文语言包、数据库连接是不同步骤,不应一律归为“Docker 出问题”。

初始化管理员,再接入正式域名

在自己的电脑上建立 SSH 转发,将示例账号和服务器地址换成实际值:

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
ssh -L 18000:127.0.0.1:8000 your-user@your-server-ip

保持连接,浏览器访问 `http://127.0.0.1:18000`。Paperless-ngx 3.2.1 的首次访问会提示创建超级用户,按页面完成初始化。因为配置里提前设置了正式域名,回环地址访问如果被主机或来源检查拦截,可以临时在 `docker-compose.env` 增加:

ini;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
PAPERLESS_ALLOWED_HOSTS=127.0.0.1,localhost
PAPERLESS_CSRF_TRUSTED_ORIGINS=http://127.0.0.1:18000

修改环境文件后,需要重新创建应用容器才会读到新值:

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
docker compose up -d webserver

这些设置与 `PAPERLESS_URL` 产生的值合并,因此不需要把正式域名改成回环地址。完成初始化并确认正式域名可访问后,移除临时加入的本地访问设置,再重新创建应用容器。这样首次设置账户的页面不会先暴露在公网。

准备正式域名,添加指向 VPS 的 A 记录,并确认已有 AAAA 记录有效。国内地域的公开网站要满足相应备案与接入要求。宿主机已有 Caddy 时,在现有配置中增加站点块:

caddyfile;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
docs.example.com {
    reverse_proxy 127.0.0.1:8000
}

按照自己现有的 Caddy 管理方式校验并重载配置。使用常规 systemd 安装、配置路径为 `/etc/caddy/Caddyfile` 时:

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

证书签发需要域名与网络满足验证条件。防火墙开放正式 Web 入口,保留 SSH 管理入口,不必公开应用、数据库和消息代理端口。此例的 Caddy 在宿主机上运行;容器里的 `127.0.0.1` 指向容器自己,采用容器反向代理时要改连接方式。

打开 `https://docs.example.com`,用刚创建的账号登录。若页面能显示但提交时报 CSRF 错误,先检查域名与 `PAPERLESS_URL` 是否一致、环境文件是否已被重新读取,不要直接关闭 CSRF 保护。

导入第一批文件时,先验证中文识别

先挑少量有代表性的文件:一份带原生文字的 PDF、一份中文扫描 PDF、一张手机拍摄的清晰图片。上传后等任务完成,打开详情页,查看预览、提取出的文字和识别出的日期。再选一个原件里明确存在的词或编号搜索,确认它能指向正确文档。中文识别成功与搜索结果正确是两项检查,应分别核对,不只看任务状态。

`auto` 模式会判断 PDF 是否已有足够的文字层,存在时可能跳过 OCR。这适合原生 PDF 与扫描件混合的资料库。如果扫描软件已有错误文字层,不能只因“搜索有结果”就认定识别无误;要对照正文内容。`redo` 和 `force` 有各自代价,尤其 `force` 会把页面栅格化,可能让文件变大、文字放大后不如原件清晰,不宜作为全部文件的默认处理。

文档详情把文件预览和分类信息放在一起。核对编号或金额时,应查看原件,不只依靠搜索结果摘要。

简体中文文件识别为空或日志提示缺语言数据时,回到语言包安装和语言代码设置排查。照片中的字很小、背景复杂或有明显倾斜,则要先改善扫描质量。OCR 参数无法补回原件里已经缺失的信息。

标签先从用途明确的少量分类开始,例如“待核对”“已报销”;文档类型用“发票”“合同”“说明书”;往来单位记录开票方或合同对方。不要把每个日期和文件名都做成标签。归档以后还能调整分类,没有必要第一天就建几十个空标签。

投递目录会清空文件,权限也要另设

文件可以在网页上传,也可以放到宿主机的 `consume` 目录。目录中的文件被成功接收后,会从该目录移走并存入 Paperless 管理的文档存储。因此,向这个目录投递副本;不要把电脑里唯一一份材料所在的目录直接挂载成消费目录。

初期先使用网页上传,熟悉处理情况后再连接扫描仪或同步任务。若投递目录位于 NFS 等不支持文件变动通知的文件系统上,可能需要设置轮询;具体见安装文档的文件系统说明。不要把文件迟迟未导入直接理解为 OCR 慢。

多用户使用时,每个人建立自己的账号。超级用户能看全部文档,适合维护,不适合用来验证普通成员的隔离效果。日常使用可以建立普通账号,根据需要给文档查看、上传或编辑权限。

尤其注意消费目录导入:官方说明,这种文档默认没有所有者和额外对象权限;无所有者对象会对有相应应用访问权限的用户可见。团队使用前,应通过工作流安排所有者或文档权限,并用两个普通账号分别检查可见范围。给标签设置权限,也不会自动限制带该标签的文档。

文档有自己的所有者、查看与编辑权限。它与用户能否进入应用、能否操作标签等全局权限是两层设置,详见用户与权限说明。

分享链接会提供另一条访问路径。发送之前确认文件内容和有效期,不要因为日常登录有密码就默认所有分享都受相同限制。

导出文档之后,还要保存部署配置

从项目目录执行:

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
cd ~/paperless
docker compose exec -T webserver document_exporter ../export

导出器会写出文档、缩略图和记录元数据的 `manifest.json` 等内容,容器内的 `../export` 对应宿主机项目下的 `export`。成功导出后,把整个导出结果复制到独立存储,另外私密保存 Compose 文件、环境文件和恢复所需的版本信息。

仅在这台 VPS 上留一个导出目录,无法应对实例丢失。导出本身也包含敏感文档和元数据,不能放进公开网页目录。默认导出不会清理以前导出中已经删除的旧文件;需要清理时先了解删除选项的范围,避免指向混有其他资料的目录。

正式资料库安排导出时,可以暂停导入、编辑和删除,让这一批备份有清楚的时间范围。数据库备份与媒体卷备份要配套保存,不能只复制数据库,就认为原件也跟着保存了。

恢复文档导出时,把完整结果放进新环境的 `export` 目录,在版本匹配、数据库和文档目录为空的安装中使用:

bash;使用前请核对本文前提。 如内容超出,可左右滑动,键盘使用方向键或 Home/End 查看。
仅复制文本,不会执行。
docker compose exec -T webserver document_importer ../export

这是新环境恢复命令,不要对正在使用的资料库直接执行。官方建议导出和导入使用相同版本;导入后检查文档数量、原件下载、中文检索和普通账号权限。确认恢复路径之后,再把新环境投入使用。参数和注意事项见导出与导入文档。

更新时把应用与数据库分开考虑

本例固定应用镜像为 3.2.1。这一版于 2026 年 9 月 20 日发布,修复了邮件抓取锁、OCR 相关依赖和缺少搜索索引文件时的重建问题。更新计划可以从官方发布记录开始,不需要因为名字叫“最新”就自动更新。

有新应用版本时,先看升级要求,完成站外备份,再修改应用镜像标签,执行拉取与启动。升级可能运行数据库迁移,出问题时不能保证只把镜像换回旧标签就能恢复。数据库和文档要有同一个恢复计划。

PostgreSQL 的大版本升级是另一项工作,本例的 `postgres:18` 不应随手改成未来的大版本并复用原数据卷。同理,更新 `.env` 中的密码只改变传入容器的值,不会改动已有数据库账户。需要变更密码时,应同步修改数据库账户与应用连接设置。

平时观察导入队列、日志、磁盘空间和失败任务。OCR 期间网页变慢,可以先减少同时处理的文件数量,检查资源占用,再决定是否增加任务并发或升配。接入批量扫描和邮件收集前,先用少量文件核对导入、检索、权限与恢复。