Skip to content
Dev Tools Beginner Tutorial

Stop Managing .env Files by Hand: Per-Project Env with direnv

Set up direnv so environment variables and virtualenvs load when you cd in and vanish when you leave.

Lenn Voss
Lenn Voss
Cloud & Infrastructure Writer · Aug 17, 2026 · 4 min read
Stop Managing .env Files by Hand: Per-Project Env with direnv

What you'll build / learn

You'll set up direnv so that the moment you cd into a project, its environment variables and a project-local Python virtualenv load automatically — and unload the instant you leave. No more source .env, no more forgetting which shell has which credentials.

Prerequisites

  • macOS or Linux with bash, zsh, or fish. Verified against direnv 2.37.1 on macOS (zsh) with Python 3.13.
  • Homebrew on macOS, or apt on Debian/Ubuntu.
  • Python 3 on your PATH (only for the virtualenv step).

1. Install direnv

macOS:

brew install direnv

Debian/Ubuntu:

sudo apt install direnv

Anything else (installs the latest release binary):

curl -sfL https://direnv.net/install.sh | bash

Confirm it landed:

direnv version
2.37.1

2. Hook it into your shell

direnv works by running before every prompt, so it needs a shell hook. Add one line at the end of your shell config — it must run after anything else that manipulates your prompt (oh-my-zsh, git-prompt, rvm):

# ~/.zshrc
eval "$(direnv hook zsh)"

For bash, put eval "$(direnv hook bash)" in ~/.bashrc; for fish, put direnv hook fish | source in ~/.config/fish/config.fish. Then restart your shell:

exec $SHELL

3. Create your first .envrc

direnv looks for a .envrc file in the current directory (and its parents). It's plain bash:

mkdir myapp && cd myapp
echo 'export API_URL=https://api.example.com' > .envrc

You'll immediately see:

direnv: error /Users/you/myapp/.envrc is blocked. Run `direnv allow` to approve its content

That's the security model, not a bug — direnv refuses to execute any .envrc you haven't explicitly approved, so a cloned repo can't silently run code on cd. Approve it:

direnv allow
direnv: loading ~/myapp/.envrc
direnv: export +API_URL

Every time you edit .envrc it gets blocked again until you re-run direnv allow. Shortcut: direnv edit . opens it in $EDITOR and re-allows on save.

4. Load your existing .env file

Keep your existing .env (your framework and Docker Compose still read it) and have direnv load it too. Replace .envrc with:

# .envrc
export API_URL=https://api.example.com
dotenv_if_exists

dotenv_if_exists is from direnv's stdlib — it parses .env into the environment and doesn't complain when the file is missing (new clones won't have it). Test it:

echo 'DATABASE_URL=postgres://localhost:5432/myapp_dev' > .env
direnv allow

Commit .envrc, keep .env git-ignored — the split is exactly shared config vs. secrets.

5. Auto-activate a per-project Python

.envrc can do more than variables. Add:

layout python3

Run direnv allow, and direnv creates a virtualenv under .direnv/python-3.13/ and puts it on your PATH — activated on entry, deactivated on exit, no source .venv/bin/activate ever again. (Node users: use node reads .nvmrc, but needs $NODE_VERSIONS pointing at installed versions.)

Verify it works

Walk in and out of the project:

cd ~/myapp
direnv: loading ~/myapp/.envrc
direnv: export +API_URL +DATABASE_URL +VIRTUAL_ENV ~PATH
echo $DATABASE_URL && which python
postgres://localhost:5432/myapp_dev
/Users/you/myapp/.direnv/python-3.13/bin/python
cd ~
direnv: unloading

echo $DATABASE_URL now prints nothing — that unload on exit is the whole point.

Troubleshooting

direnv: error /path/.envrc is blocked. Run direnv allow to approve its content — Normal after creating or editing .envrc. Run direnv allow in that directory. If it's constant, use direnv edit . instead of your editor.

Nothing prints when you cd in — The hook isn't running. Check that the eval "$(direnv hook zsh)" line is the last line of ~/.zshrc (prompt frameworks loaded after it will clobber it), then exec $SHELL. direnv status reporting No .envrc or .env loaded while you're sitting next to a .envrc confirms the hook is the problem.

direnv: error .envrc file not found when running direnv allow — You're in a directory without a .envrc, or you only have a .env. direnv doesn't load bare .env files by default; add a .envrc containing dotenv_if_exists.

A variable from .envrc is empty in your shell — You wrote FOO=bar instead of export FOO=bar. Plain assignments are local to the .envrc script; only exported variables reach your environment. (.env files don't need export — the dotenv parser exports everything.)

Next steps

Skim direnv stdlib (or the man page): PATH_add bin to expose project scripts, source_env to share config across repos, watch_file to reload when other files change, plus layouts for Ruby, Go, and more. If you use a version manager like mise or nix, both integrate with direnv so .envrc also pins your entire toolchain per project.

Sources & further reading

  1. direnv - unclutter your .profile — direnv.net
  2. Setup the hook — direnv.net
  3. Installation — direnv.net
  4. direnv-stdlib man page — direnv.net
  5. direnv releases — github.com
Lenn Voss
Written by
Lenn Voss · Cloud & Infrastructure Writer

Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading