排查 Docker Compose PWD 变量导致 Waline 数据库挂载异常
问题描述
最近发现博客评论系统(Waline v3)的后台管理页面可以正常打开,但登录时一直提示“用户名或密码错误”。试了试密码重置功能,页面显示“请稍后再试”,也搞不定。前台评论区域也不显示已有评论,整个系统看起来像是丢了所有数据一样。
这种情况在自部署项目中不算少见,我去 GitHub 和技术社区搜了一圈,发现类似问题还真不少——大都是因为 SQLite 数据库文件被意外清空或者指向了空文件,应用启动时自动创建了空表结构,导致原有用户数据全部“消失”,所以才会出现登录失败、重置失败这些看起来莫名其妙的问题。
问题原因
排查下来发现,根本原因是 Docker Compose 中 ${PWD} 变量在不同运行目录下解析出的值不同,导致 Volume 挂载路径异常。
我的 docker-compose 配置是这样的:
注意这个 ${PWD}/data。${PWD} 是一个预定义的环境变量,它的值取决于你执行 docker compose 命令时所在的目录,而不是 compose 文件所在的目录。
- 在正确目录下执行(
/docker/data/waline/)→${PWD}解析为/docker/data/waline→ 挂载/docker/data/waline/data:/app/data→ 数据库正常 - 在其他目录下执行(比如
/data)→${PWD}解析为/data→ 挂载/data:/app/data→ 但/data目录下只有一个 0 字节的空 waline.sqlite,容器读到的就是个空数据库
这就能解释为什么数据“丢了”——不是文件没了,而是挂载到了错误的位置。
Docker Compose 变量替换逻辑
根据 Docker 官方文档,Compose 在解析变量时遵循以下规则:
当没有显式设置
--env-file时,Compose 会按以下顺序确定项目目录:
--project-directory如果设置了的话- 否则,第一个通过
-f/--file指定的 Compose 文件所在的目录- 否则,当前 shell 的工作目录(
PWD)
同时,环境变量优先级如下(从高到低):
docker compose run -e命令行传入environment或env_file属性中从 shell 或.env文件插值的变量- Compose 文件中
environment属性直接设置的值 - Compose 文件中
env_file属性引用的值 - Dockerfile 中
ENV指令设置的值
来源: Docker Docs - Environment Variables Precedence
也就是说,${PWD} 的值完全取决于你在哪个目录敲下 docker compose up。如果 compose 文件里用了 ${PWD},但每次执行的目录不固定,挂载路径就会飘忽不定,这就埋下了隐患。
排查过程
先确认后台地址
我去 Waline 官方文档(waline.js.org)查了一下,确认了:
- 管理后台路径是
<serverURL>/ui/register - 第一个注册的用户自动成为管理员
- 支持邮箱密码登录,也支持 GitHub、微博、Twitter、Facebook、QQ 等第三方 OAuth
SSH 上服务器看看
日志里发现了关键线索:
这些 404 错误说明数据库中的管理相关表不存在或为空,API 路由找不到对应的数据。
对比数据库文件
| |
正确的数据库里有 4 个用户(1 个管理员 + 3 个访客),而 /data/waline.sqlite 虽然存在但是空的。
验证 API 连通性
404 进一步确认了数据库虽然加载了,但没有用户数据。
分析 docker-compose 配置
| |
看到 volumes 里用的是 ${PWD}/data:/app/data。${PWD} 在执行 docker compose up 时会被替换为当前 shell 的工作目录。如果之前在某次操作时不在 compose 文件所在目录执行命令,${PWD} 就解析到了其他路径,Volume 也就挂载到了错误的地方。
解决过程
找到原因就好办了。先在正确目录下执行一次,确保 ${PWD} 解析到正确路径:
验证一下:
数据全在,问题解决。后台地址还是 https://<博客域名>/ui/,用管理员邮箱密码登录就行。
经验总结
- Compose 文件中慎用
${PWD}:${PWD}依赖执行命令时的当前目录,不够稳定。更推荐用相对路径(Compose 会自动以 compose 文件所在目录为基准),或者用.env文件固定变量值。 - Docker volume 挂载要特别注意:挂载点下的文件如果被意外覆盖或清空,数据说没就没了。以后得给 SQLite 数据库做个定时备份,不能裸奔。
- 排查问题先看日志:
docker logs是最直接的线索来源,比瞎猜效率高多了。 - sqlite3 挺好用的:对于 SQLite 数据库,直接查询表结构和数据,一目了然。
- 现象分析很重要:登录失败 + 重置失败 + 评论消失,这些现象凑在一起,基本就是数据库层面的问题了,而不是密码记错了或者认证服务故障。