# direnv: Per-Directory Environment Variables for Python


direnv is a shell extension that loads and unloads environment variables based on the current working directory. It reads a per-directory `.envrc` file when entering a folder and reverts the environment when leaving. Although direnv is language-agnostic, it is widely used in Python projects to activate [virtual environments](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md) and export project-specific settings without polluting the global shell.

## Key Features

- Loads variables from `.envrc` on `cd` into a directory and unloads them on `cd` out
- Provides a stdlib of layout helpers, including `layout python`, `layout python3`, `layout pyenv`, and `layout pipenv`
- Supports bash, zsh, fish, tcsh, elvish, and PowerShell
- Requires explicit per-directory authorization with `direnv allow` before any new `.envrc` runs

## Installation

direnv is distributed through most package managers:

```bash
brew install direnv     # macOS / Linux
sudo apt install direnv # Debian / Ubuntu
nix-env -i direnv       # Nix
```

After installation, add the hook to the shell startup file:

```bash
# bash (~/.bashrc)
eval "$(direnv hook bash)"

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

# fish (~/.config/fish/config.fish)
direnv hook fish | source
```

The hook runs on every prompt and applies any pending `.envrc` changes after the directory is authorized.

## Activating a Python Virtual Environment

The simplest pattern sources an existing [`venv`](https://pydevtools.com/handbook/reference/venv.md) or [`virtualenv`](https://pydevtools.com/handbook/reference/virtualenv.md):

```bash {filename=".envrc"}
source .venv/bin/activate
```

The built-in `layout python` helper goes one step further and creates the environment if it does not exist:

```bash {filename=".envrc"}
layout python python3.12
```

This places the environment under `.direnv/python-3.12/` and activates it whenever the directory is entered.

## Using direnv with uv

direnv has no built-in `layout_uv` helper as of the current release, but two short patterns cover most projects.

The first activates the [`uv`](https://pydevtools.com/handbook/reference/uv.md)-managed `.venv` directly:

```bash {filename=".envrc"}
source .venv/bin/activate
```

The second sets the project environment path explicitly so activation works under any layout:

```bash {filename=".envrc"}
export UV_PROJECT_ENVIRONMENT="$PWD/.venv"
source "$UV_PROJECT_ENVIRONMENT/bin/activate"
```

For details on overriding the venv location, see [How to customize uv's virtual environment location](https://pydevtools.com/handbook/how-to/how-to-customize-uvs-virtual-environment-location.md).

## Pros

- Project-scoped variables without manual `source` or `unset`
- Works with any shell and any tool, not just Python
- Composes with venv, virtualenv, [pyenv](https://pydevtools.com/handbook/reference/pyenv.md), [conda](https://pydevtools.com/handbook/reference/conda.md), and uv

## Cons

- Requires a one-time shell hook installation
- Each new or modified `.envrc` must be authorized with `direnv allow` before it loads
- No native uv layout yet; uv users rely on `source .venv/bin/activate` or `UV_PROJECT_ENVIRONMENT`

## Learn More

- [direnv documentation](https://direnv.net/)
- [direnv stdlib reference](https://direnv.net/man/direnv-stdlib.1.html)
- [direnv on GitHub](https://github.com/direnv/direnv)
- [How to customize uv's virtual environment location](https://pydevtools.com/handbook/how-to/how-to-customize-uvs-virtual-environment-location.md)
- [What is a virtual environment?](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md)
