配置格式

环境变量与 .env 配置管理:12 因素之外的实务细节

密钥放代码里、环境变量在不同环境间漂移、.env 被提交进 git——这类隐患几乎每个项目都会踩。本文按 12 因素原则讲清楚优先级、注入方式,以及如何安全地管理本地与 CI 的配置。

作者:巧匠团队·7 分钟阅读·更新于 2026-09-06

为什么配置要交给环境变量而不是写死在代码里

配置是指在不改代码的前提下,为不同环境(本地、测试、生产)改变行为的那部分信息:数据库地址、API 端点、密钥、日志级别等。把它们写死在代码里,等于每次环境变化都要改代码、重新部署,还会引入把测试库地址误带到生产的风险。

12 因素应用的原则核心是"配置存储与代码严格分离"。环境变量是最常见也最不依赖具体框架的载体,无论 Spring、Django 还是 Node 都能通过统一机制读取;同时它天然支持运行时注入,无需重新构建镜像就能切换环境。

代价是要管好它:一旦配置分散在几百个 key 里,命名、缺省值、是否必填都会变成维护负担,所以需要工具约定来兜底,这也是后面几节要做的事。

# 12 因素:同一份代码,靠环境变量区分行为
export DATABASE_URL="mysql://app:***@prod-db:3306/shop"
export LOG_LEVEL="info"
# 代码里不再出现连接串硬编码,只读环境变量

环境变量的优先级:别被 .env 里的旧值骗了

同名的环境变量来源很多:系统环境、shell 里 export 的、.env 文件、.env.local、Docker 的 -e、CI 的 secrets。它们之间谁覆盖谁容易让人困惑,而 dotenv 类工具一个常见的坑是"我以为改了 .env,怎么没生效"。

以 Node 的 dotenv 为例,默认行为是"不覆盖已存在的系统变量":也就是如果进程环境里已有同名变量,.env 里的值不会顶替它。这个坑的后果是你在本机 shell 里 export 过一个旧值,之后怎么改 .env 都像没改。

要彻底厘清,记住通用优先级:进程内显式赋值 > shell 环境 > .env.local > .env > 默认值。遇到"改不通"先 echo $KEY 看进程环境到底解析成了什么,再决定是覆盖还是删掉旧的。

# 查看当前生效值(字段解析,别靠猜)
printenv | grep -E '^(DATABASE_URL|NODE_ENV) ='
# Node 默认不覆盖已存在变量
# 强制覆盖需显式声明
require('dotenv').config({ override: true })

用 .env.example 立规矩、用 .gitignore 挡住密钥

真正应该提交进 git 的是配置的"模板"而不是值本身。创建 .env.example 或 .env.sample,列出所有 key、写明作用与是否必填,但用占位符代替真实值。新成员 clone 后复制一份 .env 填上自己的本地值即可,这样既统一了命名又不会把密钥带进仓库。

同时务必把真实 .env 加入 .gitignore。一旦历史提交里出现过 .env,单纯加 gitignore 无法抹除——需要在 git history 里改写(filter-branch 或 BFG)并强制推送,并立刻轮换泄露的密钥。这类事故的代价远高于预防成本。

# .env.example(提交)
DATABASE_URL=mysql://user:pass@localhost:3306/shop
API_KEY=change-me
# .gitignore(追加)
.env
.env.local
.env.*.local

按环境拆分文件,并约定合并顺序

为本地、测试、生产分别维护一套变量的方式常见,但别把所有环境的值都堆进一个 .env —— 那等于变相泄露了生产配置给所有开发看到。更稳妥的是 .env(公共)+ .env.local(仅本地覆盖)+ CI secrets(环境专属)的组合。

Node MoE框架的加载顺序要明确。以 Nuxt/Vite 生态为例,约定的顺序通常是 .env < .env.local < .env.<mode>。只要团队内部统一达成的顺序并写进 README,parse 时就按此排序叠加,就能避免"哪个文件优先级高"的口水仗。

生产环境的密钥永远不要写进仓库的 .env 文件;放在 CI 的 secrets 管理或部署平台的加密变量里,构建时注入。

# 约定顺序(写进 README):.env 最弱,最后加载的覆盖
.env                # 公共,所有环境
.env.local          # 本地覆盖,不入库
.env.staging        # 暂存环境(CI 注入)
# Nuxt CLI 会根据 mode 自动加载对应文件

验证:变量解析、缺失校验与密钥轮换

上线前做三层验证。第一层解析验证:启动应用,用统一入口打印一次所有依赖的变量名与是否存在的布尔值,确认 .env 真的被加载且当前模式加载对了文件。

第二层缺省校验:在应用启动时遍历必需变量集合,缺失一个就启动失败并给出明确错误,而不是让代码在运行到一半才炸。第三层安全验证:旁路扫描代码与仓库确认无明文密钥,并通过环境变量来源对账保证生产值来自 secrets。

密钥应定期轮换,尤其在有离职或日志泄露时。轮换流程要配合双写过渡:先让新旧密钥都能用,待全员切换后再吊销旧值,避免一次性替换导致线上中断。

# 缺失即失败的校验示例
const REQUIRED = ["DATABASE_URL", "API_KEY"];
for (const k of REQUIRED) {
  if (!process.env[k]) throw new Error(`缺少必需的环境变量: ${k}`);
}
console.log("配置校验通过");

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。