Papra 26.7.1 Docker Compose 部署指南

Papra 26.7.1 Docker Compose 部署指南:官方镜像形态选型、数据库依赖链、compose 逐项解说与首次注册登录、数据持久化、升级回滚的完整实测记录

PapraPapraDocker安装部署2026/10/31

Papra 26.7.1 Docker Compose 部署指南

目标:用官方打包发行 + Compose 编排在单机部署一套可用的 Papra 26.7.1 文档归档服务,完成首次注册登录、数据持久化验证与升级/回滚路径说明。本文所有命令与回显均来自 2026-10-31 在 Ubuntu 22.04(Docker 29.9.2 + Compose v5.6.1)上的实测。组件背景见 《Papra 26.7.1 介绍与选型指南》。

一、环境信息

项要求(实测环境)
操作系统Ubuntu 22.04
Docker / ComposeDocker 29.9.2,Compose v5.6.1
Papra26.7.1(ghcr.io/papra-hq/papra:26.7.1-root)
端口服务内监听 1221,宿主映射 8134
数据目录/opt/papra/data(绑定挂载到服务内 /app/app-data)
硬件1C2G 起步(SQLite 单机形态实测空闲内存占用约 100MB 量级)
外部依赖无(默认内嵌 SQLite,无需外挂数据库/消息队列)

二、镜像选型说明

官方发行源为 ghcr.io/papra-hq/papra(备用同步源 corentinth/papra),标签分两族(2026-10-31 查询官方标签列表):

标签说明建议
26.7.1-root / 26.7.1-rootlessCalVer 版本化固定标签生产推荐,升级时机完全自主可控
latest-root / latest-rootless / latest浮动最新标签仅测试评估用
0.0.1 ~ 0.9.6(-root/-rootless)0.x 早期系列勿用
  • root 形态:服务内以 root 运行,绑定挂载数据目录无需处理 UID 映射,上手最简单。
  • rootless 形态:服务内以非特权用户运行,更安全,但要求宿主数据目录对映射 UID 可写(配合 --user $(id -u):$(id -g) 或 user: 字段)。
  • 26.7.1 构建信息(服务内环境变量实测):PAPRA_VERSION=26.7.1、GIT_COMMIT=3078ac59e953ee9a96e8f5ad7a967ae39e594037、BUILD_DATE=2026-10-22T21:42:32+02:00。

三、前置条件

  1. 已安装 Docker 与 Compose v2 插件:
docker --version
# Docker version 29.9.2, build a82e528
docker compose version
# Docker Compose version v5.6.1
  1. 生成部署密钥的工具(系统自带 openssl 即可)。
  2. 确认宿主 8134 端口空闲:
ss -ltn | grep 8134
# 无输出即空闲
  1. 规划数据目录 /opt/papra/data(Papra 全部状态——数据库与文档原文件——都在这一个目录内)。

四、部署步骤

4.1 生成部署密钥并建目录

mkdir -p /opt/papra/data && cd /opt/papra
openssl rand -hex 48
# ee7eddeee820b3742fa4a583979c8e4d1f0aebd22faf5a580b0382c8ab10547332ffd4f228ce48bced95b0e7f772272d

⚠️ 注意:AUTH_SECRET 是会话签名与加密的基础密钥,务必用上面的命令生成自己的随机值并妥善保管;更换它会使全部已有登录会话与 API Key 失效(实测 26.7.1 行为)。上面的示例值仅用于演示环境。

4.2 编写 docker-compose.yml

在 /opt/papra/docker-compose.yml 写入(以下为实测通过的完整文件):

name: papra-2671-stack
services:
  papra:
    image: ghcr.io/papra-hq/papra:26.7.1-root
    container_name: papra-2671-app
    restart: unless-stopped
    ports:
      - "8134:1221"
    environment:
      - APP_BASE_URL=http://192.168.1.200:8134
      - AUTH_SECRET=${AUTH_SECRET}
    volumes:
      - /opt/papra/data:/app/app-data
    networks:
      - papra-net
networks:
  papra-net:
    ipam:
      config:
        - subnet: 10.89.57.0/24

并准备同目录 .env 承载密钥(避免明文进编排文件):

echo 'AUTH_SECRET=ee7eddeee820b3742fa4a583979c8e4d1f0aebd22faf5a580b0382c8ab10547332ffd4f228ce48bced95b0e7f772272d' > /opt/papra/.env
chmod 600 /opt/papra/.env

4.3 启动并观察首次迁移

cd /opt/papra && docker compose up -d
docker compose ps

实测回显:

NAME             IMAGE                                STATUS             PORTS
papra-2671-app   ghcr.io/papra-hq/papra:26.7.1-root   Up About an hour   0.0.0.0:8134->1221/tcp, [::]:8134->1221/tcp

首次启动会自动执行全部数据库迁移(服务日志节选,实测 28 个迁移逐个执行):

$ pnpm migrate:up:prod && pnpm start
$ tsx src/scripts/migrate-up.script.ts
{"level":"info","message":"Database directory missing, created it","timestampMs":1793411729783,"namespace":"database-services","data":{"scriptName":"migrate-up","isDryRun":false,"dbUrl":"file:./app-data/db/db.sqlite","dbDir":"./app-data/db","dbPath":"./app-data/db/db.sqlite"}}
{"level":"debug","message":"Running migrations","timestampMs":1793411729789,"namespace":"migrations","data":{... "migrationsToRunCount":28,"existingMigrationsCount":0}}
{"level":"info","message":"Migration run successfully","timestampMs":1793411729794,"namespace":"migrations","data":{"scriptName":"migrate-up","isDryRun":false,"migrationName":"initial-schema-setup"}}
...(其余 26 个迁移逐条 "Migration run successfully",节选)...
{"level":"info","message":"All migrations run successfully","timestampMs":1793411729901,"namespace":"migrations","data":{"scriptName":"migrate-up","isDryRun":false}}
$ node dist/index.js
{"level":"info","message":"Starting application","timestampMs":1793411731049,"namespace":"app-server","data":{"processMode":"all","isWebMode":true,"isWorkerMode":true}}
{"level":"info","message":"Creating emails services","timestampMs":1793411731052,"namespace":"emails.services","data":{"processMode":"all","driverName":"logger"}}
{"level":"debug","message":"Task persistence driver initialized","timestampMs":1793411731058,"namespace":"tasks:services","data":{"processMode":"all","driverName":"memory"}}
...(注册 5 个定时清理任务:文档硬删除/邀请过期/组织清理/KV 过期清理/权益复核,节选)...
{"level":"info","message":"Server started","timestampMs":1793411731086,"namespace":"app-server","data":{"processMode":"all","port":1221}}

日志出现 Server started ... port 1221 即部署完成。

4.4 首次注册(实测)

浏览器打开 http://192.168.1.200:8134 进入引导页注册第一个账号;等价的 API 实测如下:

curl -s -X POST http://192.168.1.200:8134/api/auth/sign-up/email \
  -H 'Content-Type: application/json' \
  -d '{"name":"admin","email":"admin@papra.local","password":"q1w2e3r4"}'

实测回显(HTTP 200):

{"token":"agfwmVM59tWupGYD79fqzcvwQD76oAJm","user":{"name":"admin","email":"admin@papra.local","emailVerified":false,"image":null,"createdAt":"2026-10-31T01:57:37.091Z","updatedAt":"2026-10-31T01:57:37.091Z","twoFactorEnabled":false,"id":"usr_v63ltj99aqbnbjq8jitdemkl"}}

4.5 登录验证(实测)

curl -s -X POST http://192.168.1.200:8134/api/auth/sign-in/email \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@papra.local","password":"q1w2e3r4"}'

实测回显(HTTP 200):

{"redirect":false,"token":"gh4CcLULlNzjUtNJ5ZK5tb6Iacb5eTRa","user":{"name":"admin","email":"admin@papra.local","emailVerified":false,"image":null,"createdAt":"2026-10-31T01:57:37.091Z","updatedAt":"2026-10-31T01:57:37.091Z","twoFactorEnabled":false,"id":"usr_v63ltj99aqbnbjq8jitdemkl"}}

首个账号注册后系统自动创建默认组织;上传第一份文档、打标签、创建 API Key 等操作的逐条实测见《Papra 26.7.1 REST API 使用详解》。

⚠️ 注意:生产环境请关闭公开注册(AUTH_IS_REGISTRATION_ENABLED=false),并在部署后尽快改掉演示密码;关闭后新用户注册请求会收到 EMAIL_PASSWORD_SIGN_UP_DISABLED 错误(实测回显见常见问题 11.2)。

五、数据持久化说明

Papra 的全部状态收敛在一个数据目录(对应服务内 /app/app-data,由 PAPRA_CONFIG_DIR 指定):

/opt/papra/data/
├── db/
│   └── db.sqlite          # 全部业务数据(文档索引/标签/用户/组织/分享链接…)
└── documents/
    └── org_tqq4cqy4g6ui3iwvitpgucgh/       # 按组织 ID 分目录
        └── originals/
            ├── doc_fi8no66puuek7em2chpe37ep.pdf   # 文档原文件
            ├── doc_htp3s4dxqs1jnnoepbw3xk8u.txt
            └── doc_v8bhjdd7fty5o3xf6zeuxlt2.png
  • 文件名即文档 ID(doc_ 前缀 + 24 位小写字母数字),目录结构默认为 {组织ID}/originals/{文档ID}.{原始后缀}(可通过 DOCUMENT_STORAGE_KEY_PATTERN 自定义,见配置篇)。
  • 实测该实例在 5 份文档(含 1 份 PDF)时数据目录总大小 860K,可估算磁盘规划。
  • 备份:小规模直接停写备份(docker compose stop 后打包 /opt/papra/data)最稳妥;不停机备份需用 SQLite 在线备份工具处理 db/db.sqlite,并连同 documents/ 一起打包。
  • 恢复:还原数据目录后 docker compose up -d,服务启动时迁移框架检测到 schema 已存在会跳过全部迁移(日志中 existingMigrationsCount 为全部迁移数)。

六、环境变量表

Compose 最小可用集(全部经实测;完整参数体系见《Papra 26.7.1 配置参数详解》):

变量必填实测取值说明
AUTH_SECRET是openssl rand -hex 48 生成会话签名/加密密钥;更换即全员登出
APP_BASE_URL是http://192.168.1.200:8134对外访问地址,决定分享链接 url、邮件中的链接等
DATABASE_URL否file:./app-data/db/db.sqlite(默认)换成 libsql:///postgres:// 连接串即切换外部数据库
DOCUMENT_STORAGE_FILESYSTEM_ROOT否./app-data/documents(默认)本地文件存储根目录,必须在挂载目录内
PAPRA_CONFIG_DIR否./app-data(默认)数据根目录(数据库+文档)
EMAILS_DRY_RUN否true只把邮件打到日志不真发,默认可保持
DOCUMENTS_OCR_LANGUAGES否eng(默认)中文扫描件建议 chi_sim,eng
DOCUMENT_STORAGE_MAX_UPLOAD_SIZE否26214400(默认 25MB)超限上传实测返回 413,见常见问题

七、配置参数说明

逐项解读 4.2 编排文件:

  • name: papra-2671-stack:显式指定 Compose 项目名,避免默认目录名变化导致项目漂移。
  • container_name: papra-2671-app:固定实例名,便于日志与监控检索。
  • restart: unless-stopped:开机自启与异常自动拉起(手动 stop 的除外)。
  • ports: "8134:1221":服务内固定监听 1221(PORT 变量可改),宿主侧 8134 按需更换;APP_BASE_URL 必须与宿主侧端口一致,否则分享链接/邮件链接会指向错误地址。
  • environment:密钥经 ${AUTH_SECRET} 从 .env 注入,编排文件可入库、密钥不入库。
  • volumes: /opt/papra/data:/app/app-data:唯一持久化路径,见第五节。
  • networks.papra-net:显式固定网段 10.89.57.0/24,防止与内网其它网段冲突;单机部署下也可省略 networks 块使用默认网络。

八、配置文件挂载

Papra 26.7.1 无配置文件,全部配置走环境变量(官方 Configuration 文档只提供 .env 变量清单,2026-10-31 查询)。两种注入方式:

  1. Compose environment: 直接列出(推荐少量变量时)。
  2. 编排文件引用 .env(AUTH_SECRET=${AUTH_SECRET}),变量本体放同目录 .env 文件——本文采用此方式;.env 同样会被 Compose 自动加载为变量替换源。

无 YAML/JSON 配置挂载需求,也不需要挂载 Tesseract 语言包(OCR 语言随发行包内置,切换语言仅需 DOCUMENTS_OCR_LANGUAGES)。

九、等价 docker run

与 4.2 编排等价的最小单命令(官方文档形态,适合临时验证):

docker run -d \
  --name papra-2671-app \
  --restart unless-stopped \
  --env APP_BASE_URL=http://192.168.1.200:8134 \
  --env AUTH_SECRET=<你的密钥> \
  -p 8134:1221 \
  -v /opt/papra/data:/app/app-data \
  ghcr.io/papra-hq/papra:26.7.1-root

rootless 变体追加 --user $(id -u):$(id -g) 并保证数据目录属主一致。

十、常用运维命令

cd /opt/papra
docker compose ps                 # 实例状态(回显见 4.3)
docker compose logs -f --tail 100 # 跟踪日志(迁移/任务/HTTP 报错都在这里)
docker compose restart            # 重启实例
docker compose down               # 停止并移除实例(数据目录保留)

升级(CalVer 固定标签策略下):

# 1. 先备份数据目录
tar -czf /backup/papra-data-$(date +%F).tgz -C /opt/papra data
# 2. 改 docker-compose.yml 中的 image 标签为新版本号
# 3. 拉取并滚动
docker compose pull && docker compose up -d
# 4. 看日志确认迁移执行完成(新版本可能新增迁移,逐条 Migration run successfully)
docker compose logs -f | grep -E 'Migration|Server started'

回滚:把 image 标签改回旧版本号后 docker compose up -d。⚠️ 若新版本已执行过数据库迁移,降级可能因 schema 不兼容而失败——回滚前用第五节的备份还原数据目录是唯一稳妥路径。

十一、常见问题

Q1:上传大文件报错? 默认单文件上限 25MB(26214400 字节)。实测上传 27MB 文件:

HTTP=413
{"error":{"message":"Document size too large.","code":"document.size_too_large"}}

调大 DOCUMENT_STORAGE_MAX_UPLOAD_SIZE(单位字节,设 0 为不限制)后重启生效。

Q2:注册接口返回 EMAIL_PASSWORD_SIGN_UP_DISABLED? AUTH_IS_REGISTRATION_ENABLED=false 时的预期行为,实测回显(HTTP 400):

{"message":"Email and password sign up is not enabled","code":"EMAIL_PASSWORD_SIGN_UP_DISABLED"}

注意该错误没有外层 error 对象,与业务接口错误形态不同(better-auth 端点风格)。开放注册期间建议配合 AUTH_ALLOWED_EMAIL_DOMAINS 限定域名。

Q3:首次启动迁移失败/数据库文件损坏? 确认 DATABASE_URL 指向的目录存在且可写(日志中 Database directory missing, created it 表示框架自动建目录);db/db.sqlite 损坏时从备份还原整个数据目录。

Q4:上传的扫描件搜不到内容? 检查 DOCUMENTS_OCR_LANGUAGES 是否包含对应语言(中文 chi_sim);对已入库文档可在界面触发"重新处理",或按 REST API 篇调用 reprocess 接口(默认限流 100 次/小时/组织)。

Q5:APP_BASE_URL 配错会怎样? 分享链接 url 字段、邮件内链接都会拼错(分享链接实测直接返回 APP_BASE_URL 拼接的完整地址)。改正确后已有分享链接需重建或用 PATCH 更新。

十二、参考链接

本专栏姊妹篇:《Papra 26.7.1 介绍与选型指南》、《Papra 26.7.1 配置参数详解》、《Papra 26.7.1 REST API 使用详解》。

Papra 26.7.1 Docker Compose 部署指南 - Quzuo