Paperless-ngx 部署实战:PostgreSQL + Redis 文档库与固定公网访问
前言
真正让纸质文件变麻烦的,往往不是数量本身,而是"以后还能不能找得到"。合同、发票、收据、扫描件和临时拍下来的照片一旦分散在文件夹、聊天记录和抽屉里,想回头定位某一份资料时,时间都耗在翻找上。Paperless-ngx 的思路正好相反:先把文档集中进一个自托管系统,再通过 OCR、标签、联系人和搜索把它们变成可以管理的数字档案。对我来说,这类系统最有价值的地方,不是演示页看起来多智能,而是上传之后能不能真的留下结构、以后能不能重新找到、服务出了问题能不能判断卡在哪一层。
这次我按实际部署链路来走,不把它包装成"上传以后什么都自动完成":先准备 PostgreSQL 和 Redis,用 Docker Compose 启动 Paperless-ngx、Tika 与 Gotenberg,确认 9981 页面能访问,再实际注册账号、上传文档、修改信息和新增联系人;本地流程跑通后,再用 cpolar 把 9981 提供到公网,并切换到固定二级子域名 paperless。数据库、任务队列、文档处理和远程入口会分开说明,哪些是项目能力、哪些是这篇实际验证到的结果,也尽量不混在一起。这样后续继续做 OCR、自动分类或更复杂工作流时,至少底座已经清楚。

1. Paperless-ngx 适合解决什么问题?
Paperless-ngx 是一个开源、自托管的文档管理系统,目标是把扫描件、PDF、图片等资料集中保存,再通过 OCR、标签、联系人、日期和搜索等方式整理。
它常见的能力包括:
- OCR 识别扫描件和图片文字;
- 标签和分类;
- 全文搜索;
- PDF、JPEG、PNG、TIFF 等格式处理;
- consume folder 自动导入;
- Web 页面管理;
- 多用户和权限;
- API、邮件、定时任务等自动化扩展;
- 与 Tika、Gotenberg 等组件配合处理文档。
项目介绍部分还列出了:
- 前端:Angular
- 后端:Python + Django
- OCR:Tesseract
- Redis:任务队列与缓存
- Apache Tika:内容提取
- Gotenberg:PDF 渲染
数据库部分在这篇里存在两种口径:功能介绍中列出了 SQLite、PostgreSQL 和 MariaDB/MySQL,而后面的部署前提又明确写成"仅支持 PostgreSQL ≥ 12"。这次实际部署配置使用的是 PostgreSQL + Redis,所以后续操作全部按这条实际链路继续。
2. 部署前先确认资源和 Docker
这套方案给出的最低硬件要求是:
| 组件 | 要求 |
|---|---|
| CPU | 2 核以上(建议 4 核) |
| 内存 | ≥ 2 GB(建议 4 GB,尤其处理大量 PDF/OCR) |
| 存储 | 至少 10 GB 可用空间 |
| 磁盘类型 | SSD 推荐 |
Docker 和 Docker Compose 需要先准备好。
检查命令保持如下:
shell
# 检查 Docker
docker --version # ≥ 20.10
# 检查 Docker Compose
docker compose version # ≥ v2.5(或 docker-compose ≥ 1.29)
网络方面,主机至少要能够在首次部署时拉取镜像;如果 PostgreSQL 或 Redis 不在同一套容器网络里,还要确保对应地址可以访问。
3. 这次数据库按 PostgreSQL 来准备
当前部署流程要求:
- PostgreSQL ≥ 12
- Redis 作为任务队列和缓存
Redis 可以使用 Compose 内的容器,也可以连接已有 Redis。
这次直接在 Compose 里启动 Redis,而 PostgreSQL 使用外部数据库。
4. 先创建 Paperless 数据库用户
进入 PostgreSQL 的 psql 命令行,并以 postgres 身份登录后,执行:
shell
-- 1. 创建用户(角色)
CREATE USER paperless WITH LOGIN PASSWORD 'Sj***520!';
-- 2. 创建数据库(如果还没建)
CREATE DATABASE paperless OWNER paperless;
-- 3. (可选)授予权限
GRANT ALL PRIVILEGES ON DATABASE paperless TO paperless;
这里创建了:
- 用户:
paperless - 数据库:
paperless - 数据库所有者:
paperless

数据库账号最好单独创建,不直接使用 postgres 超级用户。这样权限范围更容易控制,后面排查数据库操作也更清楚。
这里还有一个非常需要留意的细节:上面的 SQL 密码写的是:
Sj***520!
而后面的 Compose 配置里 PAPERLESS_DBPASS 使用的是另一个字面值。数据库实际连接时,这两个位置必须与真实 PostgreSQL 用户密码保持一致,否则 Paperless-ngx 无法正常连接数据库。
5. 开始部署 Paperless-ngx
先确认 Docker:
shell
docker --version

接着创建目录:
shell
mkdir -p /docker/paperless
chmod -R 777 /paperless
这里创建的是:
/docker/paperless
但权限命令作用于:
/paperless
两个路径并不相同。真正执行权限修改前,先确认自己当前准备操作的目录,尤其 chmod -R 777 是递归权限操作,不适合在路径没核对时直接执行。
6. 创建 Docker Compose 配置
创建并编辑:
shell
vi docker-compose.yml
Compose 内容如下:
shell
version: "3.6"
services:
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:2.19.6
restart: unless-stopped
ports:
- "9981:8000"
volumes:
- ./data:/usr/src/paperless/data
- ./media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
environment:
# 改为指向内部 redis 容器
PAPERLESS_REDIS: redis://:my_redis_password@redis:6379
PAPERLESS_DBHOST: 192.168.42.140 # 数据库仍用外部
PAPERLESS_DBUSER: paperless # 输入刚建好的账号密码
PAPERLESS_DBPASS: Sjixin520!
PAPERLESS_DBNAME: paperless
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass my_redis_password
# 如果需要持久化,可加 volume
tika:
image: apache/tika:latest
restart: unless-stopped
gotenberg:
image: gotenberg/gotenberg:8.19
restart: unless-stopped
这份配置包含四个服务:
Paperless-ngx Web
- 镜像:
ghcr.io/paperless-ngx/paperless-ngx:2.19.6 - 外部端口:
9981 - 容器端口:
8000 - 数据目录:
./data - 媒体目录:
./media - 导出目录:
./export - 导入目录:
./consume
Redis
- 镜像:
redis:7-alpine - 密码:
my_redis_password - 端口:
6379
Tika
- 镜像:
apache/tika:latest - 服务地址:
http://tika:9998
Gotenberg
- 镜像:
gotenberg/gotenberg:8.19 - 服务地址:
http://gotenberg:3000
PostgreSQL 使用外部地址:
192.168.42.140
数据库名和用户名都是:
paperless
配置里的密码字段是:
PAPERLESS_DBPASS: Sjixin520!
这里不要忽略前面数据库用户创建时使用的密码,两边真正运行时需要匹配。

7. 启动并先看日志
执行:
shell
docker-compose up -d
然后查看 Web 服务日志:
shell
docker logs -f paperless-webserver-1

日志正常以后,再访问:
http://IP:9981

能够看到 Paperless-ngx 页面,说明 Web 服务、容器和数据库等基础链路至少已经进入可用状态。
8. 注册账号并进入文档管理页面
第一次进入后先注册用户。

然后登录。

登录以后可以调整页面布局。

我更愿意先把界面和账号准备好,再开始传文档,因为后面真正要验证的是"文档进来以后能不能管理"。
9. 上传一份文档做实际验证
进入上传入口,添加文档。

上传完成后,打开文档详情。

页面里可以继续修改和编辑文档相关信息。
这一步至少确认了:
文件上传 → 文档进入 Paperless-ngx → 页面可打开 → 信息可编辑
而不是只停留在"首页能访问"。
10. 联系人和文档信息也可以继续管理
接着创建新的联系人。

页面还提供排序、显示和编辑等管理入口。

Paperless-ngx 的 OCR、自动分类、标签和全文搜索是它的重要功能方向,但这篇实际截图重点展示的是账号、上传、文档详情、联系人和页面管理,因此不把所有自动识别能力都写成已经逐项实测完成。
11. 本地文档库跑通以后,再处理公网访问
到这里,Paperless-ngx 已经可以在本地 9981 页面管理文档。
如果只在当前局域网中使用,到这里已经够用。
如果人在外面还需要查文档或维护资料,再给 Web 页面补公网入口更合理。
这里 cpolar 只负责:
把 Paperless-ngx 的
9981Web 页面映射到公网。
PostgreSQL、Redis、OCR、Tika、Gotenberg 和文档本身仍然由原来的服务负责。
12. 安装 cpolar
执行安装命令:
shell
sudo curl https://get.cpolar.sh | sh

安装完成后检查服务状态:
shell
sudo systemctl status cpolar

服务正常以后,通过主机 IP + 9200 打开 cpolar Web 管理页面。
这里同时出现两种访问形式:
http://ip:9200
以及:
http://localhost:9200/
实际访问时,以当前设备真正可达的管理地址为准。

13. 先用随机域名验证 9981
进入【隧道管理 → 创建隧道】,配置为:
- 隧道名称:
paperless - 协议:
http - 本地地址:
9981 - 域名类型:随机域名
- 地区:
China Top

创建完成以后,到在线隧道列表查看公网地址。

从其他设备访问。

Paperless-ngx 页面能够正常打开。
这一步确认的是:
本地 9981 → cpolar 随机公网地址 → 外部浏览器访问成功。
14. 长期使用,再换固定二级子域名
如果准备长期远程管理文档,固定地址更方便。
进入预留功能。

地区选择:
china Top
这次保留的名称是:
paperless

然后回到【隧道管理 → 隧道列表】,找到对应隧道并编辑。

修改为:
- 域名类型:二级子域名
Sub Domain:填写已经保留的名称- 地区:
China Top
点击更新。

更新以后,在线隧道列表中的地址会切换成固定二级子域名。

最后再用固定地址访问。

页面可以正常打开。
总结
Paperless-ngx 真正有价值的地方,不是"把纸扫描一下"这么简单,而是把零散文档变成一套长期可管理的档案系统:上传之后还能继续编辑、关联联系人、排序和查找,后面再逐步利用 OCR、标签和自动化能力扩展。
这次实际跑通的链路是:
PostgreSQL → Paperless 用户 / 数据库 → Docker Compose → Paperless-ngx 2.19.6 → Redis 7 → Tika → Gotenberg 8.19 → 9981 → 注册账号 → 上传文档 → 编辑文档 → 联系人 → cpolar → 随机公网地址 → 固定二级子域名 paperless。
几个技术细节尤其值得记住:
- PostgreSQL 用户:
paperless - PostgreSQL 数据库:
paperless - 外部数据库地址:
192.168.42.140 - Paperless-ngx 镜像:
ghcr.io/paperless-ngx/paperless-ngx:2.19.6 - Redis:
redis:7-alpine - Tika:
apache/tika:latest - Gotenberg:
gotenberg/gotenberg:8.19 - Web 端口:
9981:8000 - cpolar 管理端口:
9200 - 隧道名称:
paperless - 固定二级子域名:
paperless
我更倾向于把这套系统理解成"先把文档收进来,再逐步让它变得可检索、可分类、可远程管理"。这样比一上来就追求所谓"全自动智能归档"更稳,也更符合这次真正展示出来的使用结果。