direnv¶
Load and unload environment variables based on the current directory.
Overview¶
direnv provides:
- Automatic loading - Activates when entering directory
- Security - Explicit allow/deny for .envrc files
- Shell integration - Works with bash, zsh, fish
- Tool integration - Python venvs, Node versions, secrets
- Unloading - Cleans up when leaving directory
Installation¶
macOS¶
Linux¶
# Debian/Ubuntu
sudo apt install direnv
# Arch
sudo pacman -S direnv
# Or from binary
curl -sfL https://direnv.net/install.sh | bash
Verify¶
Shell Integration¶
Bash¶
Add to ~/.bashrc:
Zsh¶
Add to ~/.zshrc:
Fish¶
Add to ~/.config/fish/config.fish:
Restart your shell after adding the hook.
Basic Usage¶
Create .envrc¶
cd ~/project
# Create environment file
echo 'export PROJECT_NAME="my-project"' > .envrc
# Allow the file
direnv allow
Automatic Loading¶
$ cd ~/project
direnv: loading ~/project/.envrc
direnv: export +PROJECT_NAME
$ echo $PROJECT_NAME
my-project
$ cd ~
direnv: unloading
Edit and Reload¶
.envrc Patterns¶
Basic Environment Variables¶
# .envrc
export DATABASE_URL="postgres://localhost/mydb"
export API_KEY="development-key"
export DEBUG=true
Path Additions¶
Source Files¶
# Source other files
source_env .envrc.local
source_env_if_exists .envrc.secrets
# Dot files
dotenv
dotenv_if_exists .env.local
Conditional Logic¶
# Only on specific OS
if [[ "$OSTYPE" == "darwin"* ]]; then
export MACOS=true
fi
# Check if command exists
if has docker; then
export DOCKER_AVAILABLE=true
fi
Python Virtual Environments¶
With uv¶
# .envrc
layout python-venv
# Or specify Python version
layout python-venv python3.12
# Using uv (recommended)
if has uv; then
# Create venv if needed
if [ ! -d .venv ]; then
uv venv
fi
source .venv/bin/activate
fi
Custom Layout for uv¶
Add to ~/.config/direnv/direnvrc:
layout_uv() {
if [ ! -d .venv ]; then
log_status "Creating venv with uv"
uv venv
fi
source .venv/bin/activate
}
Use in project:
With pyenv¶
Poetry Projects¶
Node.js Version Management¶
With nvm¶
Add to ~/.config/direnv/direnvrc:
use_nvm() {
local version="$1"
local nvmrc_path="$PWD/.nvmrc"
if [ -z "$version" ] && [ -e "$nvmrc_path" ]; then
version=$(cat "$nvmrc_path")
fi
if [ -z "$version" ]; then
log_error "No Node version specified"
return 1
fi
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
nvm use "$version"
}
With fnm¶
Add to direnvrc:
use_fnm() {
local version
if [ -e .nvmrc ]; then
version=$(cat .nvmrc)
elif [ -e .node-version ]; then
version=$(cat .node-version)
else
version="$1"
fi
eval "$(fnm env)"
fnm use "$version"
}
Direct Node Path¶
Secrets Management¶
From .env Files¶
From 1Password¶
# .envrc
export DATABASE_PASSWORD="$(op read 'op://Development/Database/password')"
export API_KEY="$(op read 'op://Development/API/key')"
From pass¶
Local Secrets File¶
Create .envrc.local for machine-specific secrets:
Layout Commands¶
Built-in Layouts¶
| Layout | Purpose |
|---|---|
layout python | Python virtualenv |
layout python3 | Python 3 virtualenv |
layout ruby | Ruby with rbenv/rvm |
layout go | Go workspace |
layout node | Node with nvm |
Custom Layouts¶
Add to ~/.config/direnv/direnvrc:
# Rust layout
layout_rust() {
export CARGO_HOME="$PWD/.cargo"
export RUSTUP_HOME="$PWD/.rustup"
PATH_add "$CARGO_HOME/bin"
}
# Docker Compose layout
layout_docker() {
export COMPOSE_PROJECT_NAME="$(basename "$PWD")"
export COMPOSE_FILE="docker-compose.yml"
if [ -e "docker-compose.override.yml" ]; then
export COMPOSE_FILE="$COMPOSE_FILE:docker-compose.override.yml"
fi
}
Security¶
Allow/Deny¶
# Allow current directory
direnv allow
# Deny (block) current directory
direnv deny
# Allow specific path
direnv allow /path/to/project
Whitelist Patterns¶
Create ~/.config/direnv/direnv.toml:
Security Best Practices¶
- Never commit
.envrc.localwith secrets - Use password managers for sensitive data
- Review
.envrcbefore allowing - Keep secrets in separate sourced files
# .envrc (committed)
source_env_if_exists .envrc.secrets
export APP_ENV="development"
# .envrc.secrets (not committed)
export API_KEY="sensitive-value"
Integration Examples¶
Full Python Project¶
# .envrc
# Python environment
layout uv
# Add local scripts to PATH
PATH_add scripts
# Load .env file
dotenv_if_exists
# Project configuration
export PYTHONPATH="$PWD/src"
export DJANGO_SETTINGS_MODULE="project.settings.local"
# Local secrets
source_env_if_exists .envrc.local
Full Node.js Project¶
# .envrc
# Node version from .nvmrc
use nvm
# Add binaries to PATH
PATH_add node_modules/.bin
# Load environment
dotenv_if_exists .env.development
# Project name for Docker
export COMPOSE_PROJECT_NAME="$(basename "$PWD")"
Full Docker Development¶
# .envrc
# Docker configuration
export COMPOSE_PROJECT_NAME="myproject"
export COMPOSE_FILE="docker-compose.yml:docker-compose.dev.yml"
export DOCKER_BUILDKIT=1
# Database for local dev
export DATABASE_URL="postgres://dev:dev@localhost:5432/myproject"
# Add helper scripts
PATH_add scripts
Monorepo Setup¶
# .envrc (root)
export MONOREPO_ROOT="$PWD"
PATH_add scripts
# packages/api/.envrc
source_up
export SERVICE_NAME="api"
layout uv
# packages/web/.envrc
source_up
export SERVICE_NAME="web"
use nvm
Troubleshooting¶
Not Loading¶
Slow Loading¶
# Use cached operations
# Instead of:
export PATH="$(npm bin):$PATH"
# Use:
PATH_add node_modules/.bin
Debug Mode¶
# Show verbose output
export DIRENV_LOG_FORMAT="%s"
direnv reload
# Or set in shell config
export DIRENV_LOG_FORMAT='direnv: %s'
Common Errors¶
# "direnv: error .envrc is blocked"
direnv allow
# "direnv: command not found"
# Add hook to shell config and restart
# Variables not unloading
# Ensure hook is last in shell config
Configuration Reference¶
direnv.toml¶
Create ~/.config/direnv/direnv.toml:
[global]
# Load .envrc from parent directories
load_dotenv = true
# Warn about changes
warn_timeout = "5s"
[whitelist]
prefix = [
"~/dev",
"~/work",
"~/projects"
]
Environment Variables¶
| Variable | Description |
|---|---|
DIRENV_LOG_FORMAT | Log output format |
DIRENV_BASH | Path to bash for .envrc |
DIRENV_DIR | Current .envrc directory |
DIRENV_WATCHES | File watch list |
See Also¶
- chezmoi - Dotfiles management
- 1Password CLI - Secrets from 1Password
- uv - Python package manager
- Environment Configuration - Shell environment