根本原因是Go模块缓存路径$GOPATH/pkg/mod位于只读文件系统,导致mkdir或write失败;应显式设置可写GOMODCACHE,配合GOPROXY=direct和GOSUMDB=off绕过网络校验,并启用go build -mod=vendor使用本地vendor目录。

go mod download 报错 “permission denied” 或 “no such file or directory” 写入缓存路径失败
根本原因不是网络或代理问题,而是 Go 默认把模块缓存写到 $GOPATH/pkg/mod,而无盘工作站或轻量终端(如 ChromeOS 终端、某些教育机房系统、只读根文件系统容器)的 $GOPATH 路径通常挂载在只读分区或内存临时盘,mkdir 或 write 操作直接被内核拒绝。
典型错误示例:
go: writing go.mod cache: mkdir /home/user/go/pkg/mod/cache/download: permission denied
此时 go mod download 会立即退出,连代理请求都不会发出去。
- 先确认缓存路径是否可写:
go env GOMODCACHE输出后,手动mkdir -p测试(如mkdir -p $(go env GOMODCACHE)),看是否报错 - 不要硬改
GOPATH到只读位置——那只会让问题更隐蔽;应显式指定一个可写的缓存目录,比如/tmp/go-mod-cache或用户主目录下的子目录(确保该目录已存在且有写权限) - 用
go env -w GOMODCACHE=/tmp/go-mod-cache永久覆盖缓存路径(注意:该命令需在可写 $HOME 下执行,否则go env -w自身也会失败) - 若连
$HOME都受限(如某些终端强制 home 目录为只读),就用临时环境变量方式:GOMODCACHE=/tmp/go-mod-cache go mod download
离线预装依赖时,go mod vendor 失败提示 “no module found to vendor”
这不是 vendor 命令本身的问题,而是当前目录下没有有效 go.mod,或 go.mod 中未声明任何依赖(即 require 块为空)。在无盘环境中,开发者常误以为“只要拷贝了源码就能 vendor”,但 Go 的 vendor 机制严格依赖模块元数据。
常见诱因:
-
go mod init没执行,或执行时不在项目根目录(导致生成空go.mod) - 项目里用了相对导入(如
import "./utils")而非模块路径导入,Go 不认为这是外部依赖,go mod tidy不会拉取,go mod vendor也就无物可 vend - 依赖全来自标准库(
fmt、net/http等),go mod vendor默认不打包标准库,输出目录为空是正常行为,不是错误
验证方法:go list -m all | wc -l 输出大于 1 才说明有第三方模块可 vend。若为 1,说明只有主模块自己,vendor 目录自然为空。
在只读系统中运行 go build 仍尝试连接 sum.golang.org
即使所有依赖都已缓存或 vendored,Go 1.16+ 默认仍会在构建时校验 go.sum 文件签名,触发对 sum.golang.org 的 HTTPS 请求。无外网环境下,这会导致构建卡住或报错 Get https://sum.golang.org/lookup/...: dial tcp: i/o timeout。
绕过校验的实操要点:
- 必须同时禁用校验服务和代理:设置
GOSUMDB=off,且GOPROXY=direct(不能留空,空值会被 Go 当作默认代理) - 推荐一次性生效:
GOPROXY=direct GOSUMDB=off go build;若要持久化,用go env -w GOPROXY=direct GOSUMDB=off - 注意:仅设
GOSUMDB=off不够——如果GOPROXY还是默认值,Go 仍会尝试通过代理访问 sumdb - 若项目含私有模块,还需加
GOPRIVATE=git.internal.company.com(替换为你自己的域名),否则仍会走代理
go mod tidy 在无盘环境反复重建缓存导致磁盘满
有些轻量终端虽允许写 /tmp,但其 tmpfs 限制极小(如仅 128MB),而 Go 模块缓存单个大依赖(如 k8s.io/kubernetes)就可能占数百 MB。go mod tidy 每次都会尝试下载并解压,缓存目录不断膨胀,最终触发 No space left on device。
应对策略不是清理,而是预防:
- 提前在有盘机器上完成依赖固化:用
go mod vendor导出全部依赖,再把vendor/目录连同go.mod、go.sum一起拷贝过去 - 启用 vendor 模式后,所有构建强制走本地:
go build -mod=vendor(注意:不是-mod vendor,少等号会报错) - 避免在无盘机上执行
go mod tidy或go get—— 它们本质是写操作,不是只读动作 - 若必须动态拉取,限定缓存大小:
GOMODCACHE=/tmp/go-mod-cache go mod download -x(-x可看到实际写入路径,便于监控)
最易被忽略的一点:vendor 模式下 go.sum 仍会被校验,所以 GOSUMDB=off 依然必要——哪怕你已经把所有代码都放在本地了。

















