Paperclip 数据库怎么配:内嵌 Postgres、本机 Docker 与外接托管库的取舍

2026-08-17

把 Paperclip 装起来之后,第一个绕不过去的问题不是建公司、也不是接 Agent,而是:数据落在哪。它管的是 Agent 的任务、审批、活动日志、成本记录,这些东西一旦跑起来就是有状态的,选错了存储形态,后面搬家会很难受。

官方部署文档在这件事上说得比较干脆:Paperclip 通过 Drizzle ORM 使用 PostgreSQL,一共给了三种把数据库跑起来的方式。而且三种之间的切换不靠改代码、也不靠什么模式开关,只认一个环境变量 DATABASE_URL

下面按官方文档的口径把三种模式拆开讲,顺带把「什么时候该从默认的内嵌库挪走」这件事说清楚。所有配置项和命令都照文档原样给,没有我们自己实测的数字——我们没有部署过这套系统,只讲文档写明的机制。

先看判定表:三种模式各自对应什么场景

官方文档给了一张切换表,逻辑简单到有点反直觉——它不看你部署在哪,只看 DATABASE_URL 这个变量的值长什么样:

DATABASE_URL生效的模式
不设置内嵌 PostgreSQL
postgres://...localhost...本机 Docker PostgreSQL
postgres://...supabase.com...托管 Supabase

文档同时强调了一句很关键的话:不管跑在哪种模式下,Drizzle 的 schema 都是同一份,位置在 packages/db/src/schema/。也就是说这三种模式差的是数据库实例本身在哪、由谁托管,表结构定义并不分叉。这一点决定了后面迁移的难度基本落在「怎么把数据搬过去」,而不是「schema 要不要改」。

如果你还没决定整个实例跑哪种部署形态,建议先看三种部署模式怎么选,数据库这一层的选择往往是跟着部署形态一起定的。

模式一:内嵌 PostgreSQL,零配置默认项

不设 DATABASE_URL 的时候,服务端会自动起一个内嵌的 PostgreSQL 实例。文档给的启动方式就是最普通的一条:

pnpm dev

首次启动时,服务端会依次做四件事:

  1. 创建 ~/.paperclip/instances/default/db/ 作为存储目录;
  2. 确认名为 paperclip 的数据库存在;
  3. 自动跑迁移;
  4. 开始对外提供服务。

四步都是自动的,不需要你手工建库、也不需要手工执行迁移。数据在重启之间是持久的——这点文档专门写了一句,因为「内嵌」两个字容易让人误以为是内存库、重启就没。想把它整个重置,官方给的办法就是把那个目录删掉:

rm -rf ~/.paperclip/instances/default/db

这条命令没有二次确认,删掉就是全清,做实验的时候留意一下。

还有一个容易踩空的点:Docker 快速启动路径默认用的也是内嵌 PostgreSQL。换句话说,你按 Docker 部署那条路把容器拉起来,并不等于数据库自动就变成了独立的 Postgres 服务——不显式设 DATABASE_URL,它还是走内嵌那套。这跟很多人对「上 Docker = 数据库独立成一个容器」的默认预期不一样,值得先确认一下再往里灌数据。

另外要说一处文档自身的措辞不一致:这篇部署文档的 frontmatter 摘要里写的是 Embedded PGlite vs Docker Postgres vs hosted,用的词是 PGlite;而正文通篇写的是 embedded PostgreSQL。两处说法对不上,文档正文没有解释这个差异,我们也不替它推断内嵌实现到底是哪一种。真要依赖这一层的具体行为(比如扩展支持、并发能力),建议以代码和实际运行结果为准,别只看文档措辞。

模式二:本机 Docker 起一个完整的 PostgreSQL

想在本地跑一个完整的 PostgreSQL 服务,官方给的是仓库自带的 compose 文件:

docker compose up -d

文档写明这会在 localhost:5432 上起 PostgreSQL 17。然后把连接串写进环境文件:

cp .env.example .env
# DATABASE_URL=postgres://paperclip:paperclip@localhost:5432/paperclip

注意这里的用户名、密码、库名三者都是 paperclip,是 compose 里配好的默认值,仅适用于本机开发场景。

跟内嵌模式最大的差别在这里:schema 不会自动推上去,要手工执行一次。文档给的命令是把连接串前置传给 drizzle-kit:

DATABASE_URL=postgres://paperclip:paperclip@localhost:5432/paperclip \
  npx drizzle-kit push

内嵌模式下第 3 步是「自动跑迁移」,到这里变成了显式的一条 drizzle-kit push。如果你从内嵌切过来之后发现服务报表不存在,先回头看这一步是不是漏了。

这种模式的实际用处,是让数据库变成一个你能用常规工具直接连上去的普通 Postgres:想接 psql 看表、想挂备份、想在另一个进程里读同一份数据,都比内嵌那套方便。代价是多一个需要你自己管生命周期的容器。

模式三:外接托管库,重点在两个端口和一个开关

生产环境,文档推荐的是托管 PostgreSQL,举的例子是 Supabase。三步:

  1. 在 database.new 建一个项目;
  2. 从 Project Settings > Database 里复制连接串;
  3. DATABASE_URL 写进 .env

真正需要留神的是后面这段。官方明确要求区分两种连接:迁移用直连(端口 5432),应用用连接池(端口 6543)。这不是可选建议,是文档直接给出的用法约定。

紧接着还有一个开关。如果走连接池且是事务模式(transaction mode),要通过环境变量关掉 prepared statements:

DATABASE_PREPARED_STATEMENTS=false

文档特意补了一句「no source edits needed」——意思是这件事被做成了纯环境变量,不需要你去改源码里的数据库客户端配置。至于为什么事务池模式和 prepared statements 会冲突,文档没有展开解释,这里也就不替它补原理了;实践上你只要记住「用了 6543 那个池化连接串,就把这个变量设成 false」。

除此之外还有三个可选的客户端调优项,文档说明是不设置时按驱动默认值走:

变量用途
DATABASE_POOL_MAX连接池上限
DATABASE_IDLE_TIMEOUT_SECONDS空闲超时(秒)
DATABASE_CONNECT_TIMEOUT_SECONDS连接超时(秒)

这三个的默认值文档没有写死具体数字,只说「驱动默认值适用」,所以别照抄别处的经验值往上填,先确认你用的驱动是什么行为。关于这几个变量在整份环境变量清单里的位置,可以对照环境变量清单怎么读一起看。

DATABASE_URL 之外,还有两处状态别忘了

只盯着数据库,容易漏掉另外两块也在本地落盘的东西。

第一块是数据根目录。环境变量清单里,PAPERCLIP_HOME 默认是 ~/.paperclip,描述写的是「所有 Paperclip 数据的基准目录」;PAPERCLIP_INSTANCE_ID 默认 default,用于在同一台机器上跑多个本地实例。前面出现的 ~/.paperclip/instances/default/db/ 这个路径,其实就是这两个变量拼出来的。你要在一台机器上跑两套互不干扰的实例,改的是 PAPERCLIP_INSTANCE_ID,而不是想办法给数据库改名。

第二块是文件存储。Paperclip 的上传文件(issue 附件、图片)走的是另一套可配置的存储 provider,跟数据库是分开的。默认落在:

~/.paperclip/instances/default/data/storage

官方给的两个选项是 local_disks3,前者对应本地开发与单机部署,后者对应生产、多节点、云上部署(文档举的兼容对象存储例子是 AWS S3、MinIO、Cloudflare R2)。配置入口是 CLI:

pnpm paperclipai configure --section storage

存储配置本身写在实例配置文件 ~/.paperclip/instances/default/config.json 里。

把这两块合起来看,结论是:只把 DATABASE_URL 指向托管库,并不等于这套系统就无状态了。附件默认还在本地磁盘上,实例配置也还在本地文件里。要做多节点或者要保证容器重建不丢东西,数据库和存储两条线得一起动。

一份迁移前的自检清单

如果你已经在内嵌库上跑了一段时间,准备换到外接库,按文档能确认的事情是这些:

检查项依据
schema 是否需要改不需要,三种模式共用 packages/db/src/schema/
新库的表结构从哪来本机 Docker 模式要手工 npx drizzle-kit push;托管模式用直连端口做迁移
应用连哪个连接串托管场景下用池化连接(6543),迁移用直连(5432)
走事务池要不要额外设置要,DATABASE_PREPARED_STATEMENTS=false
附件会不会跟着走不会,存储 provider 是独立配置,默认 local_disk
旧数据怎么搬官方这篇文档没有提供导出/导入步骤

最后一行是重点:这篇部署文档只讲了三种模式怎么起、怎么切,没有给从内嵌库把已有数据搬到外接库的官方流程。所以真要迁移,别指望有一条现成命令,得自己按常规 PostgreSQL 的方式处理,并且做好回滚准备。

什么时候这套配置不够用

有几种情况,这篇文档给的东西是不足以支撑决策的,得另外找依据:

要评估内嵌库能扛多少并发。 文档全程没有给任何性能、并发、数据量上限的数字,只说了「零配置」「数据持久」。想知道边界在哪,只能自己压,或者直接按生产标准走外接库。

要用 Supabase 之外的托管服务。 文档只举了 Supabase 一个例子,两个端口的用法也是按 Supabase 的形态描述的。换别家托管 Postgres,直连与池化端口怎么给、事务池模式的开关要不要开,得按那家自己的文档来对号入座。

要做高可用、读写分离、跨区域。 官方这篇里没有任何相关配置项,DATABASE_URL 就是一个单一连接串。

要在企业内网做私有访问。 那是另一条线的事情,跟数据库配置是正交的两件事。

真正建议的顺序是:先按一条命令装起来用内嵌库把流程跑通,确认组织结构、Agent、任务这些概念自己都理顺了,再决定要不要把数据库外挪。反过来先花半天配托管库和连接池,结果发现产品形态跟预期不符,那半天就白费了。

延伸阅读


本文依据 Paperclip 官方仓库(github.com/paperclipai/paperclip,MIT 协议)的 docs/ 用户文档 与 doc/ 下的规范、运维与连接器手册整理,核对日 2026-08-17。 我们没有部署或运行过 Paperclip,因此不涉及界面外观与操作手感; 部分规范文档描述的是目标架构而非当前实现,文中已就地标注,不构成对实际行为的保证。 请以仓库最新内容为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。