Docker 环境下 Paperless-ngx 中文增强版部署实战

Docker 环境下 Paperless-ngx 中文增强版部署实战

本文基于本人真实生产环境反复踩坑与验证 ,整理出一套可以直接部署、稳定运行、完整支持 Word(doc/docx)+ 中文 OCR(宋体 / 楷体等) 的 Paperless-ngx Docker Compose 方案。

如果你遇到过以下问题:

  • Word(docx)上传直接报 不支持文件类型
  • 中文 OCR 提示 chi_sim is not installed
  • Error while converting document to PDF: Connection refused
  • Docker 网络、权限、用户映射导致容器反复自杀

那么这篇文章就是为你写的。


一、整体架构设计说明

本方案并 不是简单拉一个 paperless-ngx 官方镜像 ,而是构建了一个 完整、稳定、可维护的文档处理流水线:

复制代码
┌──────────────┐
│   Browser    │
└──────┬───────┘
       │
┌──────▼───────────┐
│ paperless-ngx    │
│  (主服务)        │
└───┬─────────┬────┘
    │         │
┌───▼───┐ ┌───▼──────┐
│ Redis │ │PostgreSQL│
└───────┘ └──────────┘
    │
┌───▼────────────┐
│   Apache Tika  │  → 文档解析(doc/docx)
└───┬────────────┘
    │
┌───▼────────────┐
│   Gotenberg    │  → Word → PDF(LibreOffice)
└────────────────┘

为什么一定要 Tika + Gotenberg?

这是Paperless 官方推荐、也是线上环境唯一稳定方案。

  • Tika:识别 Word / Excel / 文本内容
  • Gotenberg:负责 LibreOffice 转 PDF
  • Paperless 本体只做编排与 OCR

少任何一个,Word 支持都会不完整。


二、核心问题与解决思路(重点)

1️⃣ Word(docx)无法上传的根因

并不是:

  • 文件类型没加
  • LibreOffice 没装

而是:

❌ Word → PDF 转换服务不可达(网络 / 依赖服务未就绪)

最终表现为:

复制代码
Error while converting document to PDF: [Errno 111] Connection refused

2️⃣ 中文 OCR 报错的真正原因

复制代码
The selected ocr language chi_sim is not installed

不是 Paperless 配置问题,而是:

❌ 官方镜像默认 不包含中文 OCR 语言包

所以必须 自定义 Dockerfile。


三、自定义 Dockerfile(中文 OCR 核心)

dockerfile 复制代码
FROM ghcr.io/paperless-ngx/paperless-ngx:latest

USER root

# 安装中文 OCR 语言包
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        tesseract-ocr-chi-sim \
        tesseract-ocr-chi-tra && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

USER paperless

说明:

  • chi_sim:简体中文(宋体、黑体等)
  • chi_tra:繁体中文
  • 不切回 paperless 用户,容器会直接启动失败

四、docker-compose.yml 完整方案解析

1️⃣ 数据库 & Redis

  • PostgreSQL 15(稳定、性能好)
  • Redis 7(任务队列)
  • 全部启用 healthcheck,避免 Paperless 抢跑

2️⃣ Paperless-ngx 主服务(关键点)

yaml 复制代码
depends_on:
  db:
    condition: service_healthy
  broker:
    condition: service_healthy
  gotenberg:
    condition: service_healthy
  tika:
    condition: service_healthy

这是解决 Connection refused 的核心配置。

Paperless 一定要在:

  • Tika
  • Gotenberg
  • Redis
  • PostgreSQL

全部健康之后再启动。

yaml 复制代码
在这里插入代码片

3️⃣ Word / OCR 相关核心环境变量

yaml 复制代码
PAPERLESS_TIKA_ENABLED: "1"
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000

PAPERLESS_OCR_LANGUAGE: chi_sim+eng
PAPERLESS_OCR_MODE: skip

含义说明:

  • skip:已有文本层不重复 OCR(效率 + 质量)
  • 中文 Word → PDF → OCR 完整链路

4️⃣ UID / GID(踩坑最多的一点)

yaml 复制代码
PAPERLESS_UID: 1000
PAPERLESS_GID: 1000

⚠️ 千万不要用 root(0:0)

否则你会看到:

复制代码
usermod: user paperless is currently used by process 1
fatal: stopping the container

5️⃣ 完成文件

yaml 复制代码
services:
  # ===== Redis 消息队列服务 =====
  broker:
    image: redis:7  # 使用 Redis 7 版本作为消息代理
    container_name: paperless-redis  # 容器名称
    restart: unless-stopped  # 容器退出时自动重启(除非手动停止)
    command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru  # 限制内存512MB,LRU淘汰策略
    networks:
      - paperless-network  # 连接到自定义网络
    healthcheck:  # 健康检查配置
      test: ["CMD", "redis-cli", "ping"]  # 使用 redis-cli ping 命令检查服务状态
      interval: 30s  # 每30秒检查一次
      timeout: 3s  # 超时时间3秒
      retries: 3  # 失败3次后标记为不健康
      start_period: 10s  # 容器启动后10秒开始健康检查

  # ===== PostgreSQL 数据库服务 =====
  db:
    image: postgres:15  # 使用 PostgreSQL 15 版本
    container_name: paperless-db  # 容器名称
    restart: unless-stopped  # 容器退出时自动重启(除非手动停止)
    environment:
      POSTGRES_DB: paperless  # 数据库名称
      POSTGRES_USER: paperless  # 数据库用户名
      POSTGRES_PASSWORD: paperless  # 数据库密码
    volumes:
      - ./data/postgres:/var/lib/postgresql/data  # 数据库数据持久化目录映射
    networks:
      - paperless-network  # 连接到自定义网络
    healthcheck:  # 健康检查配置
      test: ["CMD-SHELL", "pg_isready -U paperless -d paperless"]  # 检查数据库是否就绪
      interval: 30s  # 每30秒检查一次
      timeout: 5s  # 超时时间5秒
      retries: 3  # 失败3次后标记为不健康
      start_period: 30s  # 容器启动后30秒开始健康检查(数据库启动较慢)

  # ===== Paperless-ngx 主服务 =====
  webserver:
    # image: ghcr.io/paperless-ngx/paperless-ngx:latest  # 官方镜像(已注释)
    build:
      context: .  # 构建上下文为当前目录
      dockerfile: Dockerfile  # 使用自定义 Dockerfile 构建
    image: paperless-ngx-zh:latest  # 自定义镜像名称(支持中文)
    container_name: paperless-web  # 容器名称
    restart: unless-stopped  # 容器退出时自动重启(除非手动停止)
    depends_on:  # 依赖服务,这些服务会优先启动
      db:
        condition: service_healthy  # 等待数据库健康检查通过
      broker:
        condition: service_healthy  # 等待 Redis 健康检查通过
      gotenberg:
        condition: service_healthy  # 等待 Gotenberg 健康检查通过
      tika:
        condition: service_healthy  # 等待 Tika 健康检查通过
    ports:
      - "8000:8000"  # 端口映射:宿主机8000端口 -> 容器8000端口
    environment:
      # ----- 数据库连接配置 -----
      PAPERLESS_REDIS: redis://broker:6379  # Redis 连接地址
      PAPERLESS_DBENGINE: postgresql  # 数据库引擎类型
      PAPERLESS_DBHOST: db  # 数据库主机地址(容器名)
      PAPERLESS_DBNAME: paperless  # 数据库名称
      PAPERLESS_DBUSER: paperless  # 数据库用户名
      PAPERLESS_DBPASS: paperless  # 数据库密码

      # ===== 文档处理核心配置 =====
      PAPERLESS_TIKA_ENABLED: "1"  # 启用 Tika 文档解析服务(支持更多文档格式)
      PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000  # Gotenberg PDF 转换服务地址
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998  # Tika 文档解析服务地址

      # ----- OCR 光学字符识别配置 -----
      # OCR(中文建议开启)
      PAPERLESS_OCR_LANGUAGE: chi_sim+eng  # OCR 识别语言:简体中文+英文
      PAPERLESS_OCR_MODE: skip  # OCR 模式:跳过已有文本层的文档(避免重复识别)

      # ----- 系统性能与安全配置 -----
      # 安全 & 性能
      PAPERLESS_TASK_WORKERS: 2  # 任务处理工作进程数量
      PAPERLESS_THREADS_PER_WORKER: 2  # 每个工作进程的线程数
      PAPERLESS_TIME_ZONE: Asia/Shanghai  # 系统时区设置为上海时区

      # ----- 任务调度优化配置 -----
      PAPERLESS_TRAIN_TASK_CRON: "5 */1 * * *"  # 每小时第5分钟自动训练分类器(提高自动分类准确度)
      PAPERLESS_INDEX_TASK_CRON: "15 0 * * *"  # 每天午夜00:15进行索引优化(提高搜索速度)
      PAPERLESS_SANITY_TASK_CRON: "30 0 * * 0"  # 每周日午夜00:30进行完整性检查(检查文档一致性)

      # ----- 文件权限配置 -----
      # 宿主机用户和分组
      PAPERLESS_UID: 1000  # 容器内文件所有者的用户ID(对应宿主机用户)
      PAPERLESS_GID: 1000  # 容器内文件所有者的组ID(对应宿主机用户组)

    volumes:  # 数据卷映射(持久化存储)
      - ./data/data:/usr/src/paperless/data  # 应用数据目录(索引、缓存等)
      - ./data/media:/usr/src/paperless/media  # 文档存储目录(原始文件和归档文件)
      - ./data/export:/usr/src/paperless/export  # 导出目录(用于导出文档)
      - ./data/consume:/usr/src/paperless/consume  # 消费目录(监控此目录自动导入文档)
    networks:
      - paperless-network  # 连接到自定义网络
    healthcheck:  # 健康检查配置
      test: ["CMD", "curl", "-f", "http://localhost:8000"]  # 检查 Web 服务是否响应
      interval: 30s  # 每30秒检查一次
      timeout: 10s  # 超时时间10秒
      retries: 3  # 失败3次后标记为不健康
      start_period: 60s  # 容器启动后60秒开始健康检查(应用启动较慢)

  # ===== Gotenberg PDF 转换服务 =====
  gotenberg:
    image: gotenberg/gotenberg:8  # 使用 Gotenberg 8 版本
    container_name: paperless-gotenberg  # 容器名称
    restart: unless-stopped  # 容器退出时自动重启(除非手动停止)
    command:  # 容器启动命令参数
      - gotenberg  # 主程序
      - --chromium-disable-javascript=true  # 禁用 Chromium 的 JavaScript(提高安全性和性能)
      - --libreoffice-start-timeout=60s  # LibreOffice 启动超时时间设置为60秒
      - --api-timeout=300s  # API 请求超时时间300秒
    environment:
      DISABLE_GOOGLE_CHROME: "1"  # 禁用 Google Chrome(使用 Chromium 代替)
    networks:
      - paperless-network  # 连接到自定义网络
    healthcheck:  # 健康检查配置
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]  # 检查 Gotenberg 健康状态
      interval: 30s  # 每30秒检查一次
      timeout: 10s  # 超时时间10秒
      retries: 3  # 失败3次后标记为不健康
      start_period: 40s  # 容器启动后40秒开始健康检查(LibreOffice 启动较慢)

  # ===== Apache Tika 文档解析服务 =====
  tika:
    image: apache/tika:latest  # 使用 Apache Tika 最新版本
    container_name: paperless-tika  # 容器名称
    restart: unless-stopped  # 容器退出时自动重启(除非手动停止)
    networks:
      - paperless-network  # 连接到自定义网络
    healthcheck:  # 健康检查配置 - 简化为 TCP 端口检查
      test: ["CMD-SHELL", "timeout 5 bash -c '</dev/tcp/localhost/9998' || exit 1"]  # 检查 9998 端口是否可访问
      interval: 30s  # 每30秒检查一次
      timeout: 10s  # 超时时间10秒
      retries: 5  # 失败5次后标记为不健康(增加容错)
      start_period: 60s  # 容器启动后60秒开始健康检查(Tika 启动较慢)

# ===== 网络配置 =====
networks:
  paperless-network:  # 自定义网络名称
    driver: bridge  # 使用桥接网络驱动
    name: paperless-net  # 网络显示名称
    ipam:  # IP 地址管理配置
      driver: default  # 使用默认 IPAM 驱动
      config:
        - subnet: 172.28.0.0/16  # 子网地址范围
          gateway: 172.28.0.1  # 网关地址

五、网络配置(解决 Network unreachable)

yaml 复制代码
networks:
  paperless-network:
    driver: bridge
    name: paperless-net
    ipam:
      config:
        - subnet: 172.28.0.0/16
          gateway: 172.28.0.1

为什么要显式指定网络?

  • 避免 CentOS / iptables 残留规则冲突
  • 避免 Docker 默认网络创建失败
  • 彻底解决 [Errno 101] Network is unreachable

六、部署步骤(可直接执行)

bash 复制代码
# 1. 构建镜像
docker compose build

# 2. 启动服务
docker compose up -d

# 3. 查看状态
docker compose ps

访问:

复制代码
http://服务器IP:8000

七、最终效果验证

✔ 支持 doc / docx 上传

✔ 支持宋体 / 楷体 / 黑体中文 OCR

✔ 中文全文搜索

✔ 自动分类训练

✔ 稳定运行,无随机失败


八、总结(踩坑后的结论)

Paperless-ngx 想稳定支持中文 Word,不是装个 LibreOffice 就完事。

真正关键的是:

  1. Tika + Gotenberg 服务完整
  2. 正确的启动顺序(healthcheck)
  3. 中文 OCR 语言包
  4. 非 root 用户运行
  5. 稳定的 Docker 网络

这套方案,已经在 CentOS 7 / Windows Docker / 生产环境 全部验证通过。

可以放心直接用。

相关推荐
Mikko714 分钟前
Linux 磁盘满了但 du 找不到?LVM 扩容 df 不变?fstab 写错开不了机?三台真机实测磁盘排查
linux·运维·centos
蓝胖的四次元口袋20 分钟前
Jenkins知识梳理(2)
运维·jenkins
yumgpkpm30 分钟前
Acceldata ODP 3.3.6.4 vs CDP Private Cloud Base 7.3.2 对比
大数据·运维·服务器·hadoop·华为·zookeeper·hbase
AIGC大时代32 分钟前
LLM Agent 执行沙箱闸门:失败含义与当天最小实验
docker·沙箱·闸门·bubblewrap·llm agent
Liuqy-0540 分钟前
特殊进程——孤儿、僵尸、守护进程
linux·运维·服务器
考研保研资料分享1 小时前
自动化控制保研经验合辑:上交夏令营、北工大预推免与跨专业面试复盘(2025)
运维·面试·自动化
灯澜忆梦2 小时前
【docker】#2 | 基础命令
docker·容器·eureka
乐维_lwops2 小时前
2026选择运维监控系统时应该重点考察哪些功能?
大数据·运维·人工智能
峥无3 小时前
Linux NPTL线程:创建/终止/等待/分离/栈布局内核原理|线程下篇
linux·运维·线程