Kratos 的 env.NewSource() 仅识别全大写、下划线分隔的环境变量(如 HTTP_PORT),不支持小写、点号、短横线或自动转换;配置加载顺序决定覆盖优先级,靠前源优先;-env 参数不影响运行时配置;空值会擦除前值而非跳过。

env.NewSource() 读不到小写或带点的环境变量
Kratos 的 env.NewSource() 默认只识别全大写、下划线分隔的环境变量,比如 HTTP_PORT 能读到,但 http.port、http_port、HTTP-PORT 全部被忽略。它不会自动把 server.port 映射成 SERVER_PORT,也不会做任何格式转换。
解决方法只有两个:
- 统一改用大写+下划线命名:把
server.port对应的环境变量设为SERVER_PORT - 手动指定映射规则:用
env.NewSourceWithPrefix("APP_")或自定义env.NewSourceWithOptions(env.WithDecoder(...))注入自己的 key 转换逻辑
配置没被覆盖,其实是加载顺序反了
config.WithSource 是按参数传入顺序从左到右合并的,**靠前的源优先级更高**,后加载的会覆盖前面同名 key。很多人误以为“最后加的才生效”,结果把 env.NewSource() 放在最左边,文件配置放右边,导致环境变量永远赢不了文件。
正确写法(环境变量优先):
conf.Load(
config.WithSource(
env.NewSource(), // 第一顺位:环境变量
file.NewSource("configs/base.yaml"), // 第二顺位:基础配置兜底
file.NewSource(fmt.Sprintf("configs/%s.yaml", env)), // 第三顺位:环境专属配置(可选)
),
)
如果想让文件优先、环境变量只做局部微调,就反过来:先 file.NewSource,再 env.NewSource。
kratos run 自带的 -env 参数不作用于运行时配置
很多人习惯用 kratos run -env prod,以为这会自动加载 configs/prod.yaml 并设置环境变量。其实不是:kratos run 的 -env 只影响构建阶段的 Go 构建标签(如 //go:build prod),对 conf.Load() 完全无感。
运行时真正起效的方式只有两种:
- 显式构造配置源:在
main()里解析-envflag,然后拼进file.NewSource(fmt.Sprintf("configs/%s.yaml", env)) - 依赖系统环境变量:直接
export ENV=prod,再让env.NewSource()去读取ENV或其他你定义的变量
合并后值是 nil 或空字符串,不是没覆盖而是被清掉了
Kratos 用 mergo 合并配置,默认策略是「覆盖」,但注意:如果后加载的源里某个 key 的值是空字符串、null 或未定义,它仍会把前一个非空值给覆盖掉。比如 base.yaml 里写了 db.url: "mysql://...",而 prod.yaml 里漏写了 db.url,那合并后 db.url 就变成空——这不是没覆盖,是“覆盖成了空”。
排查建议:
- 用
conf.Data()打印最终合并后的原始 map,确认 key 是否存在、值是否为空 - 检查所有参与合并的源,确认缺失项是否真被显式声明(哪怕只是占位)
- 避免依赖“隐式 fallback”,关键配置项在每个环境文件中都应显式写出
配置多源合并这件事,表面是顺序问题,实际是数据流控制问题。最容易被忽略的,是你以为自己在“覆盖”,其实是在“擦除”。











