
本文详解 Docker 环境下 Bun 项目的热重载配置要点,重点解决 bun --watch 在挂载卷中失效的问题,指出 --hot 才是 Bun 官方推荐的热重载模式,并提供容器内文件监听、权限、信号传递与开发配置的完整实践方案。
本文详解 docker 环境下 bun 项目的热重载配置要点,重点解决 `bun --watch` 在挂载卷中失效的问题,指出 `--hot` 才是 bun 官方推荐的热重载模式,并提供容器内文件监听、权限、信号传递与开发配置的完整实践方案。
在 Docker 中为 Bun 项目启用真正有效的热重载(Hot Reload),关键在于理解 Bun 的两种运行模式差异及其在容器环境中的行为约束。
✅ --hot ≠ --watch:Bun 热重载的核心区别
Bun 提供两个常被混淆的开发参数:
- bun --watch run src/index.ts:仅监听文件变更并重启进程(full restart),不保留应用状态,且在 Docker 挂载卷(尤其是 Windows/WSL2)中极易因 inotify 事件丢失而失效;
- bun --hot run src/index.ts:启用 真正的热重载(Hot Module Replacement, HMR),底层基于 V8 的代码热替换机制,在不中断服务的前提下动态更新模块、函数和导出对象——这才是 Bun 原生支持的“热重载”。
? 正确做法:将 package.json 中的 dev 脚本从 "bun --watch run src/index.ts" 替换为:
"dev": "bun --hot run src/index.ts"
⚙️ Docker 容器内启用 --hot 的必要条件
Bun 的 --hot 模式依赖以下三项基础能力,缺一不可:
| 条件 | 说明 | 你的配置是否满足? |
|---|---|---|
| 1. 宿主机文件系统变更可被容器内 inotify 捕获 | WSL2 默认对挂载的 Windows 文件(如 /mnt/c/...)禁用 inotify 事件;必须启用 sysctl 配置或改用 WSL2 内部路径(如 ~/project) | ❗你当前 ./api/:/app 是 Windows 路径挂载 → 高概率失效 |
| 2. 容器内 bun 进程为 PID 1 | --hot 需直接接收 SIGUSR2 等热重载信号;若 CMD 被 shell 包裹(如 sh -c "bun --hot..."),信号会被拦截 | ✅ 你的 Dockerfile 使用 CMD [ "bun", "run", "dev" ] → 正确(exec 模式) |
| 3. bun.lockb 与源码严格同步 | --hot 依赖精确的模块图快照;若挂载卷覆盖了容器内 node_modules 或 bun.lockb,可能导致 HMR 元数据错乱 | ⚠️ 你使用了 volumes: - ./api/:/app,但未排除 node_modules 和 bun.lockb → 风险项 |
✅ 推荐修正配置(适配 WSL2 + Windows 开发)
✅ 步骤 1:优化 docker-compose.yml(避免 inotify 失效)
services:
api:
# ... 其他配置保持不变
volumes:
- ./api:/app
# ? 关键:显式排除 node_modules 和 lockfile,防止覆盖容器内已安装依赖
- /app/node_modules
- /app/bun.lockb
# ? 强制启用 WSL2 inotify(需在 WSL2 中执行一次)
# command: sh -c "echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf && sysctl -p && bun --hot run src/index.ts"
? WSL2 快速修复命令(在 Ubuntu 终端中运行):
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf sudo sysctl -p
✅ 步骤 2:更新 api/Dockerfile(确保最小化干扰)
FROM oven/bun:1.0.3-slim WORKDIR /app # 先复制锁文件和包声明(保证依赖一致性) COPY package*.json bun.lockb ./ RUN bun install --production=false # 显式安装 dev deps(如 bun-types) # ? 不再 COPY . ,避免覆盖已安装的 node_modules # COPY . . # 启动时挂载源码,由 volume 动态提供 CMD ["bun", "--hot", "run", "src/index.ts"]
✅ 步骤 3:验证热重载是否生效
进入运行中的容器,手动触发一次变更检测:
docker exec -it cv_api sh -c 'ls -l /app/src/index.ts && bun --hot --version'
然后修改 api/src/index.ts 中任意一行(如 console.log("hot reload test")),观察终端是否输出类似:
⚡ Hot reloading module: /app/src/index.ts ✅ Updated 1 module in 12ms
? 常见误区与避坑指南
- ❌ 不要混用 --watch 和 --hot:二者互斥,--hot 已内置文件监听,叠加 --watch 反而引发冲突。
- ❌ 不要在 Dockerfile 中 COPY . 后再挂载源码卷:这会导致 node_modules 被空目录覆盖,HMR 因找不到模块解析路径而静默失败。
- ❌ 不要通过 sh -c 启动 Bun:如 CMD ["sh", "-c", "bun --hot run src/index.ts"] 会使 shell 成为 PID 1,--hot 无法接收信号。
-
✅ 推荐调试命令:
# 查看容器内 inotify 监听状态 docker exec cv_api find /app -type d -name "src" -exec ls -la {} \; 2>/dev/null | head -5 # 检查 Bun 是否以 PID 1 运行 docker exec cv_api ps -eo pid,ppid,comm | grep bun
✅ 总结:一句话口诀
“用 --hot 代替 --watch,挂载时排除 node_modules 和 bun.lockb,WSL2 下务必调大 inotify.max_user_watches” —— 三步到位,Bun 热重载即刻生效。
完成上述调整后,你在 VS Code 或 Vim 中保存 index.ts,API 容器将秒级响应变更,无需 docker restart,真正实现类本地开发体验。











