起因
每个项目不同 env:
- 项目 A:DATABASE_URL=postgres://localhost/a, API_KEY=xxx
- 项目 B:DATABASE_URL=postgres://localhost/b, AWS_PROFILE=client
老办法:
source .env(每次开新 shell 要重 source).envrcshell script. ./envrc(容易忘)dotenvlib 在应用里读(CLI 工具读不到)
direnv 解决:进目录自动 export env,出目录自动 unset。
装
brew install direnv
apt install direnv
# shell 集成
eval "$(direnv hook zsh)" # 加到 .zshrc
用
cd ~/projects/myapp
echo 'export DATABASE_URL=postgres://localhost/myapp' > .envrc
direnv allow # 首次要批准
cd ..
# DATABASE_URL unset
cd ~/projects/myapp
# DATABASE_URL 自动 set
.envrc 是 bash script,可以做任何事。
安全
.envrc 是任意 shell code → 安全风险。
direnv 强制 direnv allow 才执行 → 你 review 后启用。
文件改了 → 重新 allow。
cd ~/some-random-project
# direnv: error .envrc is blocked. Run `direnv allow` to approve its content
加载 .env
# .envrc
dotenv # 加载同目录 .env 文件
或者:
dotenv .env.local
支持标准 .env 格式:
DATABASE_URL=postgres://...
SECRET_KEY=abc
venv 自动 activate
# .envrc
layout python python3.12 # 自动建 venv + activate
或者用现有 venv:
# .envrc
source .venv/bin/activate
进项目目录 → venv 自动激活 + env vars 加载。
不用记 source .venv/bin/activate。
Node 版本
# .envrc
use node 20 # mise / nvm 集成
或者:
# .envrc
PATH_add ./node_modules/.bin # 加到 PATH,能调 local tool
PATH_add 在前 → 项目 local 工具优先。
多层 .envrc
~/.envrc # 全局(如 EDITOR=nvim)
~/projects/.envrc # 所有 project 共享(如 PNPM_HOME=...)
~/projects/myapp/.envrc # 项目特定
进 myapp → 3 个 .envrc 合并(深层覆盖浅层)。
与 dotenv-cli / 应用层 dotenv
应用层 dotenv lib 只对应用进程有效。
direnv 对 shell 内任何命令 有效(如手动 psql, aws cli 等)。
混用:direnv 给 shell + 应用 dotenv 给 production deploy(不用 direnv
在 server)。
与 mise
mise 也支持 dotenv-load:
# .mise.toml
[env]
_.file = ".env"
两个工具能 overlap。我用:mise 管 runtime 版本,direnv 管 env vars。
都装 fine(启动 hook 不冲突)。
安全 - secret in git
.env 通常 gitignore,.envrc 也 gitignore 较好(避免 secret 进 git)。
template:
# .envrc.example (commit 这个)
export DATABASE_URL=postgres://localhost/myapp
export API_KEY=<your-key>
# .envrc (gitignore,每人自己 cp 自己改)
或者用 1Password CLI 拉 secret:
# .envrc
export API_KEY="$(op read 'op://Personal/myapp/api_key')"
执行 direnv 时去 1Password 拉,不 commit 实际值。
CI
CI 通常不用 direnv(用 env var injection)。但 direnv 的 .envrc 可以
被 CI 间接利用:
# CI step
source .envrc
或者把 .envrc 改成 plain .env 用 dotenv CLI 跑。
真实工作流
我每个项目 .envrc:
dotenv .env.local # local 配置
layout python # venv
PATH_add ./bin # 项目 script
PATH_add ./node_modules/.bin
export PYTHONUNBUFFERED=1
export AWS_PROFILE=$(basename "$PWD") # AWS profile = 项目名
进项目 → venv + env + AWS profile + PATH 一键就绪。
1 秒切换项目 context。
与 docker compose env_file 对比
# docker-compose.yml
services:
app:
env_file: .env
compose 自动 load .env。但只对 compose 启动的容器有效。
shell 里手动 psql / debug 仍要 direnv。
性能
direnv hook 在 shell prompt 前跑(PROMPT_COMMAND)。
单次 < 5ms(cache)。
首次进新目录加载 .envrc 慢点(看 .envrc 复杂度)。
踩过的坑
-
没装 hook:source 了 hook 但没重启 shell → direnv 不生效。
exec zsh或开新 terminal。 -
.envrc改了忘 allow:env 用旧值。每次改后direnv allow。
工具会提示。 -
export 漏:bash 习惯
KEY=value而非export KEY=value→ 子
进程拿不到。.envrc内必须 export。 -
dotenv 顺序:
.envrc内dotenv后又export覆盖。后者
生效。 -
cd 进子目录 env 不变:direnv 按 .envrc 文件位置加载。子目录
没 .envrc 沿用父。OK 但有时困惑。
登录后参与评论。