You switch from one project to another and forget to update your environment — now you're running commands against the wrong database, or a script fails because a variable from the last project is still set. Manually `source`-ing an env file every time you change directories is exactly the kind of chore people forget to do.

**direnv** removes the chore. It loads a project's environment variables the moment you `cd` into its directory and unloads them the moment you leave — automatically, with no command to remember. It hooks into your shell and reads a file called `.envrc` from the current directory tree.

## How direnv works

direnv sits between you and your shell. When you enter a directory, it looks for an `.envrc` file, runs it, and applies any variables it exports to your current shell session. When you leave, it reverts those changes. The result: each project gets exactly the environment it expects, and only while you're in it.

Crucially, direnv won't run an `.envrc` until you explicitly approve it — more on that below — so cloning a repo can't silently execute code in your shell.

## Installing direnv

```bash
# macOS / Linux
brew install direnv

# Debian / Ubuntu
sudo apt install direnv
```

Then hook direnv into your shell so it can load and unload automatically:

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

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

## Your first .envrc

Create an `.envrc` file in your project root:

```bash
export DATABASE_URL="postgres://localhost/myapp_dev"
export NODE_ENV="development"
export API_BASE="http://localhost:4000"
```

The first time direnv sees it, it refuses to load it and warns you:

```
direnv: error .envrc is blocked. Run `direnv allow` to approve its content
```

This is the security model: **direnv never runs an `.envrc` you haven't explicitly trusted.** Review the file, then approve it:

```bash
direnv allow
```

From now on, entering the directory loads those variables and leaving unsets them. Edit the file and direnv asks you to `allow` again, so a change can't sneak in unreviewed.

## Loading an existing .env file

If you already keep variables in a `.env` file, direnv can load it with one line instead of duplicating everything:

```bash
# .envrc
dotenv
```

You can also layer setup on top — add a directory to your `PATH`, load language-specific tooling, or inherit a parent directory's environment with `source_up`:

```bash
# .envrc
dotenv
PATH_add ./bin
source_up
```

## Keep secrets out of plaintext

`.envrc` and `.env` are convenient, but they're plaintext — don't commit real secrets in them. Add `.envrc` to your `.gitignore`, or keep only non-sensitive defaults in the committed file. For secrets you *do* want version-controlled, [encrypt your .env files with dotenvx](https://www.deployhq.com/guides/dotenvx) and let direnv load the decrypted values, so nothing sensitive lands in the repository in the clear.

## direnv and DeployHQ

direnv is a **local development** convenience: it shapes your shell environment while you work. Your deployment environment is configured separately, and that separation is intentional — you don't want your laptop's `.envrc` deciding what production sees.

On the DeployHQ side, set the values your app needs at deploy time as config files or environment values in your project settings, and add any build steps to your [build pipeline](https://www.deployhq.com/features/build-pipelines). Then enable [automatic deployments](https://www.deployhq.com/features/automatic-deployments) so each push builds and ships with that server-side configuration. The mental model: direnv handles *your machine*; DeployHQ handles *the deploy* — and dotenvx-encrypted files are how a secret safely travels between the two.

## direnv vs a bundled tool

direnv does one job — per-directory environment — and does it cleanly. If you're also managing language runtime versions and project tasks, you may not want three separate tools: you can [handle env, runtimes, and tasks together with mise](https://www.deployhq.com/guides/mise), which includes the same auto-loading behavior direnv provides. Choose direnv when environment loading is all you need; reach for mise when you want one config file to cover more.

Ready to ship with the right configuration on every deploy? [Create a free DeployHQ account](https://www.deployhq.com/signup) and connect your repository in minutes.