升级与备份
升级会更新应用代码,也可能更新数据库表结构。部署旧的镜像 tag 可以恢复应用代码,但无法撤销数据库表结构变更。
自部署总览说明了三个平台在全新安装时执行迁移的时机。升级采用相同的时机,但还需要保护数据库中的现有数据。
每次升级前先备份
请备份 Postgres。 看板、反馈、投票、评论、更新日志条目、账号、登录会话和相似反馈检测使用的向量数据都存储在 Postgres 中。没有数据库备份,就无法恢复这些数据。
pg_dump --format=custom "$DATABASE_URL" > feedlog-$(date +%F).dump应用运行期间也可以执行此命令,无需停机。备份完成后,先在临时数据库中恢复一次,确认备份可用。
上传的文件需要单独备份。 文件位置取决于存储配置:
| 存储方式 | 文件在哪 | 升级时的风险 |
|---|---|---|
S3 兼容存储(配了 S3_*)、Cloudflare R2、Vercel Blob | 你自己的存储桶或存储服务 | 部署不会修改这些文件。请单独备份 |
| 未配置存储(Docker) | 容器内的 /app/.data | 除非该路径挂载了卷,否则删除容器时也会删除这些文件 |
未设置 S3_* 变量时,上传功能会使用本地文件系统驱动,并将文件写入容器。仓库中的 compose.yml 在 /app/.data 挂载了命名卷(named volume),因此删除容器时会保留这些文件。如果使用单行 docker run 命令部署但没有添加 -v,文件不会保留,而 Docker 升级中的 docker rm 步骤会将它们删除。
请同时备份环境变量,尤其要保留 BETTER_AUTH_SECRET。更改该值会使所有用户的现有登录会话失效,并使待使用的密码重置和邮箱验证链接失效。
升级
docker pull ghcr.io/linkcraftstudio/feedlog:v0.2.0
docker stop feedlog && docker rm feedlog
docker run -d --name feedlog -p 3000:3000 \
... same env vars as before ... \
ghcr.io/linkcraftstudio/feedlog:v0.2.0
docker logs -f feedlog # wait for "Database migration completed"
# With the bundled compose file: edit the image: tag, then
docker compose pull app && docker compose up -d appgit pull upstream main
git push # Vercel rebuilds; migrations run in the build stepgit pull upstream main
pnpm install
pnpm build:cf && pnpm deploy:cf
# Then visit the site as a signed-in admin; see below各平台的完整步骤保存在代码仓库中,并与正在部署的版本一起发布: Docker · Vercel · Cloudflare Workers
该部署哪个 tag
| Tag | 指向什么 |
|---|---|
latest | main 分支的最新构建。发布新构建时会变化 |
v0.2.0 | 指定的正式发布版本。不会变化 |
0.2 | 该次要版本中最新的补丁版本。发布新补丁时会变化 |
sha-1a2b3c4 | 指定的一次提交。不会变化 |
试用 FeedLog 时可以使用 latest。生产环境应使用 vX.Y.Z 或 sha- 开头的 tag。否则,宿主机因其他原因重启后,再次运行 docker compose up -d 可能会意外部署新版本并执行迁移。因此,仓库自带的 compose.yml 使用固定的版本 tag。
升级时的数据库迁移
迁移时机与全新安装相同,但升级时还需要在迁移失败后处理现有数据。
- Docker:容器启动时执行待处理的迁移。Postgres advisory lock 会让迁移按顺序执行,因此两个容器同时启动时不会重复执行同一条迁移;第二个容器会等待,并在获得锁后确认没有待处理的迁移。该锁不能防止旧版本应用使用新的表结构,因此升级时应先停止旧版本,再启动新版本,不要逐个替换多个副本。将流量切换到新容器之前,请等待日志中出现
Database migration completed。进程开始运行后/health就会返回 200,此时迁移可能尚未完成。 - Vercel:迁移在 Nuxt 构建之前执行。迁移失败会使本次部署失败,上一个部署继续提供服务,因此失败的表结构变更不会进入生产环境。
DATABASE_URL必须同时设置在 Production 和 Preview 作用域中,而且 Vercel 构建机器必须能够连接你的 Postgres。 - Cloudflare Workers:Worker 没有启动阶段,迁移只能在收到请求时执行。因此,升级还需要完成下文所述的手动操作。
Cloudflare Workers 升级需要管理员确认
运行 pnpm deploy:cf 后,如果该版本包含尚未执行的迁移,Worker 会将站点切换到 setup 状态:所有页面请求跳转到 /setup,所有 /api/ 请求返回 503 和 NOT_INITIALIZED。/setup 页面随后尝试执行迁移。因为这是升级而不是首次安装,该接口要求调用者是已登录的管理员。未登录的访问者会收到 "Administrator required"。此限制用于防止普通访问者触发数据库表结构变更。
在 setup 状态下,除 /setup、/api/auth/**、/health 和静态资源之外,其他请求都会跳转,其中也包括显示登录弹窗的页面。如果新版本 Worker 上线时没有管理员登录会话,就无法通过界面登录。
部署之前先登录
运行 pnpm deploy:cf 之前,请打开实例并确认当前已以管理员身份登录。部署不会使登录 cookie 失效。部署完成后打开任意页面,Worker 会将请求重定向到 /setup,执行迁移,再返回原页面。如果未登录就进行部署,站点只能提供 /setup,而该页面会返回 "Administrator required"。
如果 /setup 无法继续,请直接请求 /api/_migrate/status。该接口会返回 state、已执行的迁移数量(applied)和预期的迁移数量(expected)。
回滚
回滚应用代码有三种方式:部署上一个镜像 tag;在 Vercel 控制台重新部署上一个版本;或者切换到旧 commit 后再次运行 pnpm deploy:cf。
FeedLog 不支持回滚数据库表结构。迁移记录保存在跟踪表中,并且只向前执行;FeedLog 不提供反向迁移。已发布的迁移包含删除索引、删除唯一约束和将字段改为 NOT NULL 等操作。旧版本应用使用此类表结构时,可能出现行为异常或无法启动。
回滚前,请先确认属于以下哪种情况:
- 新版本有 bug,但没有执行任何迁移。 部署旧 tag 即可,无需恢复数据库。大部分回滚属于这种情况。
- 新版本执行了迁移。 部署旧 tag,并且恢复升级前的 Postgres 备份。升级后、回滚前写入的数据会丢失,因此应在业务低峰期升级。
Docker 和 Vercel 的部署日志会显示已执行的迁移。Cloudflare 部署完成后可通过 /api/_migrate/status 查询。
要恢复的是数据库,不只是镜像
仅将容器回退到上一个 tag 不会撤销迁移。如果新版本修改过数据库表结构,还必须恢复升级前的数据库备份。
常见报错
容器启动后立即退出,并反复重启。 迁移失败。docker logs feedlog 中的 Database migration failed 行包含具体错误。此时表结构已完成部分变更。请先解决失败原因再重启,或者恢复备份后重新升级。
容器重启后页面返回 500,稍后恢复。 进程开始监听端口时,迁移仍在执行。请等待日志中出现 Database migration completed,再将流量切换到该容器。/health 返回 200 不代表表结构已就绪。
Vercel 部署在迁移步骤失败。 上一个部署仍在提供服务。常见原因是 DATABASE_URL 未在 Production 作用域中启用,或者 Vercel 的构建 IP 无法连接数据库,例如 Postgres 部署在 VPN 后的自有机房中。
/setup 显示 "Administrator required",但无法登录。 部署前没有建立管理员登录会话。请使用仍保留有效管理员会话的浏览器,再刷新 /setup。
Docker 升级之后上传的图片返回 404。 该实例使用本地文件系统存储,但 /app/.data 没有挂载卷,因此 docker rm 同时删除了文件。这些文件无法恢复。下次升级前请先配置 S3_* 存储。参见配置外部服务。
升级后所有用户都退出了登录。 BETTER_AUTH_SECRET 已更改,通常是因为新部署生成了新值,而不是沿用原值。恢复原值后,原有会话会恢复。