过去几个月,我在 SayCraft 的生产部署上踩了不少坑。不是那种"看文档就能避免"的坑——是那种你以为部署成功了、用户却在跑旧代码的坑。
记录下来,给自己备忘,也给同样用 Docker Compose 做生产部署的人一点参考。
BuildKit 缓存:新源码,旧产物
docker compose up --build,看着日志跑完,一切正常。但线上的页面就是没变。
我花了一段时间才意识到问题出在 BuildKit 的层缓存上。Next.js 的 next build 输出被缓存了,Docker 判断 COPY 层没变就直接复用。源码确实是新的,但构建产物还是上一次的。
诊断方法很土:SSH 到服务器,grep 客户端 JS chunk 里某个只有新版本才有的 keyframe 名。找不到,说明跑的还是旧构建。修复也很直接:docker compose build --no-cache flashdesign-web,强制全量重建。
这个问题的恶心之处在于——它不报错。一切看起来都正常,日志是绿的,容器是新的,但里面装的是旧东西。
BuildKit COPY 报错:静默失败的构建
比缓存复用更狠的一个:BuildKit 在 COPY packages 阶段缓存报错,构建直接失败了。但 Docker Compose 不在乎。它拿旧镜像重新建了个容器,deploy 脚本照常退出码 0。
新代码没上线,部署却报成功。
我是在排查一个"明明改了为什么没生效"的问题时发现的。最后的修复是 docker builder prune -f 清掉缓存再重新部署,然后在打包后的 bundle 里 grep 一下确认新代码确实存在。从那以后,每次部署后我都会做这个验证。
Postgres 密码 Volume-lock:改了等于没改
这个坑反复咬了我好几次。
Postgres 官方镜像的 POSTGRES_PASSWORD 环境变量只在 data volume 首次初始化时生效一次。之后你怎么改 .env 都没用,卷里的密码不会跟着变。结果就是 28P01 认证错误反复出现。
更乱的是,我们的 Docker Compose 用的是 PG_PASSWORD(带一个 fallback 默认值 12345678),而不是直接用 POSTGRES_PASSWORD。两个变量名一字之差,值还可能不一样。env 文件里改了一个,另一个没跟上,密码就对不上了。
最终的修复方案是在 Compose entrypoint 里做 self-heal:利用 127.0.0.1 的 trust 认证自动对齐密码,再加一个 scram healthcheck gate 确保密码一致后才放行流量。听起来复杂,但这是唯一能保证"不管谁重启、不管从哪重启都不会出 28P01"的方案。
NEXT_PUBLIC 变量:构建时烤死的
Next.js 的 NEXT_PUBLIC_* 变量在 next build 时就写进了静态 HTML 和 JS bundle。运行时改环境变量没有任何效果。
所以在 Dockerfile 里,你必须同时声明 ARG 和 ENV,再在 Compose 的 build args 里把值传进去。漏掉任何一步,构建出来的前端就会带着错误的 API 地址上线。这不是 Docker 的问题,是 Next.js 的设计决定,但在容器化部署里特别容易踩。
deploy 脚本的两个致命问题
我们的 deploy-prod.sh 里用了 rsync .(注意那个点)。如果你 cd 到了某个子目录再跑脚本,rsync 只会上传那个子目录的内容,并且删除服务器上其他所有文件。对,所有。
另一个问题:脚本在远程服务器上用后台方式跑 docker compose build,本地 SSH 连接一断开脚本就报成功退出了。但远程构建可能还要跑好几分钟。脚本报成功不等于部署成功。你得自己去看容器状态或者 curl 一下健康检查接口。
双分支部署:跑错脚本的代价
SayCraft 有两套生产环境:us 分支部署到海外服务器 saycraft.ai,china 分支部署到国内服务器。两个 deploy 脚本,两台服务器,两套 env 文件。
2025 年 6 月,我在 us 分支上跑了 deploy-prod.sh(国内的部署脚本)。结果把海外分支的代码推到了国内服务器,覆盖了正在运行的国内版本。
没有任何报错。脚本不会检查你当前在哪个分支。
总结
这些问题有个共同点:它们都不会在你面前报错。缓存复用不报错,构建失败不报错,密码不匹配要等到有请求才报错,脚本跑错分支不报错。Docker 给你一个"一切正常"的假象,你得自己去验证。
我现在的做法是:每次部署后 grep bundle 确认新代码在、curl 健康检查确认服务起来了、跑之前 pwd 确认在项目根目录。很原始,但有效。
SayCraft 是一个用对话构建 Web 应用的工具,感兴趣可以看看 saycraft.ai。