使用Vercel+Supabase 无服务器部署Umami
前言
在此之前,我博客和手头几个站的访问统计一直是用自己镇江服务器上 docker 跑的 umami+PgSQL 这套方案本身用了挺久也没出过啥乱子,但是最近正在将本增效(笑),无意间看见 Umami 是 NextJS 写的,于是异想天开看看能不能无服务器部署。官方文档
本文就顺手记录一下从零部署 + 迁移,的完整流程,以及遇到的一些奇奇怪怪的问题。

平台与区域怎么选
在正式动手之前,我其实先对比了几个主流的无服务器方案,结论如下:
- Cloudflare Workers:最开始其实想能不能塞进 Worker 里一把梭,去翻了一圈发现社区虽然有过适配 PR 但已经被关掉了(详见 Discussion #3170)。主要是打包完之后体积直接超了免费版 3 MiB 的限制,而且 Prisma 跑在上面也很容易把免费版的 CPU 时间干爆,遂放弃。
- Netlify:虽然官方有写 Netlify 的部署指南,但现在新账号改成了逆天(shab)的信用点制度(免费档一个月 300 点,每次生产构建还要扣 15 点)。Umami 最要命的地方在于每次访客打开网页触发
/api/send上报,函数都得等着远端 PostgreSQL 写完返回才算结束,等待数据库响应的 I/O 时间也算在函数执行时长里。万一哪天访问量稍微大点或者数据库响应慢了,直接给你一锅端了 - Vercel Hobby + Supabase Free:Vercel 免费档每个月给 100 GB 流量和 100 万次函数调用,就算真超了一般也是降速而不是直接给你拔网线;配合 Supabase 免费档提供的 PostgreSQL 实例,拿来跑个人博客的统计完全是绰绰有余。
WHY NOT USE NEON?
好问题,其实是因为 neon 已经跑了几个项目了,再往里加怕不够用( 其实完全可行
区域选择
跟之前写 Lambda + MongoDB Atlas 部署 Twikoo 时提到的一样,无服务器部署这类带数据库的应用,核心延迟不在于 “人到 Vercel”,而在于 “Vercel 到 PgSQL”:
研究了一下 umami 的架构,发现访客每次上报事件、或者你在后台点开看板,边缘函数都要连一次数据库。如果函数在亚洲、数据库在美西,每次请求光跨洋握手和查询就得几百毫秒,不仅后台打开卡得怀疑人生,还会白白浪费函数执行时长
所以我这里把两边全部锁定在了新加坡: Vercel 函数执行区域选了 sin1,Supabase 选了 Asia Pacific
最终整套架构的流量走向大概是这样:
flowchart LR Sites["各站点统计脚本 /maimaidx"] CN["umami.icybit.cn"] CF["CloudFront CDN"] Vercel["Vercel sin1<br/>Umami"] SB["Supabase ap-southeast-1<br/>PgSQL"]
Sites --> CN CN --> CF CF -->|"/api/* 转发 Authorization"| Vercel CF -->|"/maimaidx 缓存 24h"| Vercel Vercel -->|"DATABASE_URL 连接池 :6543"| SB Vercel -->|"DIRECT_DATABASE_URL 直连 :5432"| SB注意 sb 的 project 区域是随机分的,总而言之你等他创建完再去 vercel 改也不迟。如果你脸黑给你随到了澳大利亚直接删 project 再建一个就行。
准备工作
你需要准备下面这些东西:
- 一个 GitHub 账号
- 一个 Vercel 账号
- 一个 Supabase 账号
- 一个准备拿来绑定的自定义域名,如果你也打算优化国内访问速度,可以再备一个 AWS 账号用来套 CloudFront
1. 创建 Supabase 数据库
首先打开 Supabase 官网 登录,进入控制台后点击 New project 创建一个新项目。
- Project Name:随意,比如
umami。 - Database Password:设置一个强密码并记下来(注意密码里最好只用字母和数字,不要带
@、#、/这种特殊符号) - Region:这里建议选 Asia Pacific,和后面 Vercel 的函数区域保持一致就行。

等个一两分钟数据库初始化完成后,点击页面顶部的 Connect 按钮打开连接信息面板,切换到 ORMs -> Prisma(或者直接看 Transaction pooler 和 Session pooler)。

WARNING
这里有一个 Vercel 等无服务器项目连 PgSQL 极大可能遇到的问题:我们必须同时准备两条数据库连接串!
DATABASE_URL(运行时连接池地址,端口6543):Serverless 函数每次唤醒都会新建数据库连接,如果直连数据库,并发稍微高一点点就会瞬间把他的 60 最大连接数占满。所以日常运行必须走端口为6543的 Transaction pooler,并且在末尾加上?pgbouncer=true&connect_timeout=10。DIRECT_DATABASE_URL(构建/迁移直连地址,端口5432):Umami 在构建(pnpm build)时会自动用 Prisma 建表和跑数据库迁移,而 Prisma Migrate 是不能在 Transaction 模式的连接池上跑的。从 Umami v3.2.0 开始原生支持了DIRECT_DATABASE_URL这个变量,专门给构建阶段跑迁移用,端口填5432。
拿到连接地址后,把你的真实密码填进去,整理出如下两条地址备用(如果你不想把表全拉在默认的 public 下,也可以像我一样在参数里指定 schema=umami):
# 运行时走 6543 连接池DATABASE_URL=postgresql://postgres.<你的项目REF>:<你的密码>@aws-1-ap-southeast-1.pooler.supabase.com:6543/postgres?schema=umami&pgbouncer=true&connect_timeout=10&sslmode=require&uselibpqcompat=true
# 构建迁移走 5432DIRECT_DATABASE_URL=postgresql://postgres.<你的项目REF>:<你的密码>@aws-1-ap-southeast-1.pooler.supabase.com:5432/postgres?schema=umami&sslmode=require&sslaccept=accept_invalid_certs如果你和我一样从旧服务器迁移数据
如果你是全新安装,直接跳到下一节部署 Vercel 就行,第一次 build 的时候 Umami 会自动把表全建好。
但如果你跟我一样,手上已经有跑了很久的旧版 Umami(比如我服务器上是 1Panel 跑的 3.1.0 + PostgreSQL 18),想把数据无损搬过来,可以按这个顺序操作:
- 先不要停旧容器,也不要改 DNS,先把旧库用
pg_dump导出并恢复到 Supabase 的umamischema 里。 - 核对一下
website、website_event、session这几张核心表的行数,确保一样 - 接着去走下面的第 2 步把最新版的 Umami 部署到 Vercel。构建时 Prisma 会自动识别旧库已有的 19 条 migration,并丝滑补齐升级的 migration
NOTE
顺带提一嘴,跨版本迁移补增量的时候建议不要 SELECT * 比如我这次操作时, Umami 从 3.1.0 升到 3.3.1 的时候,session_data 表在 23_* 那次迁移里新增了列,增量导入时建议显式写出两边的共有列,并且放在单个事务里跑,
2. 部署 Umami 到 Vercel
数据库准备好之后,部署应用本体就非常简单了。
首先打开 Umami 的官方仓库 umami-software/umami,点击右上角的 Fork 按钮,Fork 一份到你自己的 GitHub 账号下。
接着打开 Vercel 控制台,点击 Add New… -> Project,导入你刚刚 Fork 的 umami 仓库

在点击 Deploy 之前,展开 Environment Variables,把下面这四个环境变量填进去:
| 变量名 | 值 | 说明 |
|---|---|---|
DATABASE_URL |
postgresql://...:6543/postgres?... |
上一步整理的 6543 端口连接池地址 |
DIRECT_DATABASE_URL |
postgresql://...:5432/postgres?... |
上一步整理的 5432 端口迁移地址 |
APP_SECRET |
随便生成一串长随机字符串 |
用于加密会话的密钥;如果是从旧站迁移,直接把旧环境的 APP_SECRET 抄过来 |
TRACKER_SCRIPT_NAME |
maimaidx |
自定义统计脚本路径名,我的是这个 |
NOTE
为什么要加 TRACKER_SCRIPT_NAME?
Umami 默认的统计脚本路径叫 /script.js,这名字实在太显眼了,很容易被访客浏览器里的 uBlock Origin、AdGuard 等去广告插件直接当成追踪器拦截。改成一个毫不相干的名字(比如 maimaidx()),访问路径就会变成 https://你的域名/maimaidx,基本就不会被规则集误杀了
另外记得去项目的 Settings -> Functions 里,把 Function Region 改成离你数据库近的地方

最后点击 Deploy 开始构建。第一次构建大约需要两三分钟,期间会自动连上 Supabase 建表。构建完成后打开 Vercel 分发给你的 .vercel.app 域名:
- 全新安装:使用默认管理员账号
admin、密码umami登录(进去第一件事记得先改密码!)。 - 旧站迁移:直接用你原来的管理员账号密码就能登进去,之前的所有站点和历史数据都在。
如果你绑定的域名打算直接 CNAME 到 Vercel,那去项目的 Settings -> Domains 里加上你的域名,再去 DNS 服务商那边配一条 CNAME 就已经大功告成了~
3. 使用 Cloudfront 加速访问
众所周知,Vercel 默认的节点在国内部分地区的直连体验有时候比较看脸,所以我主力用的统计域名 umami.icybit.cn 选择在前面套了一层 CloudFront
关于怎么开通和白嫖 CloudFront,可以看我的往期博文,这里就不再赘述基础步骤了。但是!把 CloudFront 套在 Umami 前面有两个奇葩问题,如果不单独配规则,你会跟我一样半夜莫名其妙 401(
首先是能登录但后台疯狂报 401(Something went wrong)
刚开始把域名切到 CloudFront 之后,打开后台登录页一切正常,输入账号密码点登录(POST /api/auth/login)也能成功返回 Token。结果一进到仪表盘页面,提示 Something went wrong,打开浏览器 F12 一看,后面的 /api/me、/api/websites 等所有请求清一色全是 401 Unauthorized,并且重新登录没用。
当时先把数据库、APP_SECRET、环境变量全排查了一遍都没毛病,绕过 CloudFront 直接访问 vercel.app 原生域名也是正常的。最后终于抓到了真凶:
WARNING
CloudFront 默认会把 GET 请求里的 jwt 删除
Umami 的鉴权靠的是 jwt。登录是 POST 请求所以过了,但登录后拉取数据的接口全是 GET 请求,CloudFront 默认策略直接把 Authorization 头丢了才回源给 Vercel,Vercel 收不到凭证自然就 401 了。
解决办法:在 CloudFront 分配的 Behaviors(行为) 里,给 /api/* 单独新建一条高优先级的行为规则:
| 字段 | 填写值 |
|---|---|
| Path pattern | /api/* |
| Viewer protocol policy | Redirect HTTP to HTTPS |
| Allowed HTTP methods | GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE |
| Cache policy | Managed-Amplify-DefaultNoCookies |
这里有个细节需要注意:不是先用后付并且免费 plan 的 CloudFront 是不允许创建自定义缓存策略的,只能从 AWS 预置的托管策略里挑。
如果直接选 CachingDisabled,它又不会把 Authorization 头塞进转发策略里;所以实测下来最省事的方案就是选 AWS 自带的 Managed-Amplify-DefaultNoCookies:。
统计脚本加载慢出大芬
后台能正常进了之后,我又发现各个站点加载 https://umami.icybit.cn/maimaidx 这个统计脚本的时候特别卡
原因是因为 如果规则里把 Cookie 也算进了缓存判断,那不同访客浏览器里带的 Cookie 千奇百怪,CloudFront 就会认为每次请求都不一样,导致缓存命中率几乎为零。
解决办法:再给你的统计脚本路径单独加一条 24 小时的强制缓存规则:
- Path pattern:
/maimaidx换成你自己的 - 缓存与回源核心逻辑:完全忽略 Cookie(不把 Cookie 算进缓存键),只向源站转发
Host头,并将缓存时间设为 24 小时。
WARNING
配置这条规则的时候千万别忘了把 Host 头转发过去,我第一次改的时候漏了转发域名,结果 CloudFront 回源时不带 Host,Vercel 不知道你要访问哪个项目,直接给我的统计脚本返回 404,补上 Host 之后才恢复正常(
改完生效之后随便测一下响应头
x-cache: Hit from cloudfront就说明统计脚本已经被缓存在 CloudFront 边缘节点上了。
总结
折腾完这一套下来算是大功告成了,可以直接丢掉服务器上的旧容器了。
而且一分钱都不要捏
如果你也在自建 Umami,强烈推荐试试这套全 Serverless 的方案,有问题欢迎在评论区留言讨论喵~
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
使用Vercel+Supabase 无服务器部署Umami
https://blog.icybit.cn/posts/45- 作者
- IcyMichiko
- 发布于
- 2026-11-13
- 许可协议
- CC BY-NC-SA 4.0
评论