go mod download 在只读目录下失败是因为默认写入 $gomodcache(如 $gopath/pkg/mod),而该路径挂载为只读;解决方法是显式设置 gomodcache 到可写路径或提前预热缓存后离线部署。

go mod download 在只读目录下失败,不是 Go 本身不支持离线,而是它默认尝试写入 $GOMODCACHE,而该路径恰好挂载为只读——必须显式切换缓存位置或提前预热缓存。
为什么 go mod download 会往只读目录写东西?
Go 默认把所有下载的模块存到 $GOMODCACHE(通常是 $GOPATH/pkg/mod),这个路径由 go env GOMODCACHE 确认。如果该路径所在文件系统被挂载为只读(比如某些容器镜像、加固服务器、CI 构建环境),go mod download 就会直接报错:permission denied 或 read-only file system。
这不是权限不足(如没用 sudo),而是底层 fs 层面拒绝写入——chmod 或改属主都无效。
- 执行
go env GOMODCACHE看输出路径是否在只读分区(如/usr/local/share/go/pkg/mod) - 用
mount | grep $(dirname $(go env GOMODCACHE))检查挂载选项是否含ro -
go mod download从不“只读检查”,它直接尝试创建目录和写文件,失败即终止
临时换缓存路径:GO111MODULE=on 时有效
只要不是 GOPATH 模式(即 GO111MODULE=on),就能用 GOCACHE 和 GOMODCACHE 环境变量重定向路径——但注意:GOCACHE 控制编译缓存,GOMODCACHE 才管模块下载。
- 先确保有可写目录,比如
/tmp/go-mod-cache或$HOME/.cache/go/pkg/mod - 运行:
go env -w GOMODCACHE=/tmp/go-mod-cache - 再执行
go mod download,它就会写入新路径,不再碰原只读位置 - 验证:
ls /tmp/go-mod-cache/cache/download应有子目录生成
⚠️ 注意:这个设置是用户级持久的,下次 go mod download 仍走该路径;若需恢复默认,执行 go env -u GOMODCACHE。
离线场景:只读环境里根本不能下载,得提前打包
如果目标机器整个系统盘都是只读(比如嵌入式设备、安全沙箱),那连临时换路径都不行——因为 /tmp 可能也受限或内存临时盘太小。这时唯一办法是在有网且可写的机器上完成全部缓存,再整体拷过去。
- 在联网机器上,确保项目已
go mod tidy成功,且go list -m all输出稳定 - 执行
go mod download,然后确认go env GOMODCACHE下所有模块目录都存在(尤其检查cache/download和cache/vcs子目录) - 用
tar -cf mod-cache.tar -C $(go env GOMODCACHE) .打包(别漏.) - 解压到离线机的**完全相同路径**,且
go env GOPATH必须一致——否则 Go 根本找不到它 - 最后设
GOPROXY=off GOSUMDB=off,否则 Go 还会试图联网校验
CI/CD 构建中避免只读失败的惯用写法
很多 CI 平台(如 GitHub Actions、GitLab CI)默认工作目录或缓存挂载为只读,硬编码 GOMODCACHE 容易出问题。更稳妥的做法是结合构建阶段做缓存隔离:
- 在 job 开头加:
export GOMODCACHE=$(mktemp -d) - 后续所有
go mod命令自动使用该临时路径 - 构建完可选地
tar -cf go-mod-cache.tar -C $GOMODCACHE .上传为缓存 - 下次 job 恢复缓存后,再
export GOMODCACHE=$(pwd)/mod-cache && tar -xf go-mod-cache.tar -C $GOMODCACHE
这种写法绕开了对系统级 GOPATH 的依赖,也规避了只读挂载风险——关键是每次构建用独立缓存,不共享也不污染宿主机。
真正麻烦的不是“怎么让 Go 写进去”,而是“它默认往哪写”和“你有没有控制权”。一旦路径落在只读分区,所有补救动作都得围绕重定向或预置展开,没有中间态。











