name: direnv description: Guide for using direnv - a shell extension for loading directory-specific environment variables. Use when setting up project environments, creating .envrc files, configuring per-project environment variables, integrating with Python/Node/Ruby/Go layouts, working with Nix flakes, or troubleshooting environment loading issues on macOS and Linux.
direnv Skill
Shell extension for loading and unloading environment variables based on the current directory.
Overview
direnv enables:
- Per-project environment configurations
- Automatic loading of 12-factor app environment variables
- Isolated development environments with language-specific layouts
- Secure allowlist-based security model
- Integration with Nix, asdf, and version managers
Quick Start
Installation
# macOS
brew install direnv
# Linux (Debian/Ubuntu)
sudo apt install direnv
# Linux (binary)
curl -sfL https://direnv.net/install.sh | bash
# Verify
direnv version
Shell Hook Configuration
Zsh (~/.zshrc):
eval "$(direnv hook zsh)"
Bash (~/.bashrc):
eval "$(direnv hook bash)"
Fish (~/.config/fish/config.fish):
direnv hook fish | source
Place hook at end of file, after other shell extensions.
Basic Usage
# Create .envrc
echo 'export MY_VAR=value' > .envrc
# Allow the .envrc (required for security)
direnv allow
# Block an .envrc
direnv deny
# Force reload
direnv reload
# Check status
direnv status
Essential Commands
| Command | Description |
|---|---|
direnv allow [path] |
Allow/trust an .envrc file |
direnv deny [path] |
Revoke trust from .envrc |
direnv reload |
Force reload current .envrc |
direnv status |
Show current direnv state |
direnv edit |
Open .envrc in $EDITOR |
direnv prune |
Remove expired/revoked .envrc |
direnv version |
Show installed version |
Standard Library (stdlib)
direnv includes a powerful stdlib. Use these functions instead of raw exports.
PATH Management
# Add to PATH (prepends safely)
PATH_add bin
PATH_add node_modules/.bin
PATH_add scripts
# Add multiple paths
PATH_add bin scripts tools
# Remove from PATH
PATH_rm "*/.git/bin"
Environment File Loading
# Load .env file
dotenv
# Load specific file
dotenv .env.local
# Load if exists (no error if missing)
dotenv_if_exists .env.local
# Load parent .envrc
source_up
# Load specific .envrc
source_env ../.envrc
# Load if exists
source_env_if_exists .envrc.local
Language Layouts
# Python - creates virtualenv in .direnv/
layout python
layout python3
layout python python3.11
# Node.js - adds node_modules/.bin to PATH
layout node
# Ruby - sets GEM_HOME
layout ruby
# Go - configures GOPATH
layout go
# Perl - local::lib
layout perl
# Pipenv
layout pipenv
Nix Integration
# Load nix-shell environment
use nix
# Load Nix flake
use flake
# Specific flake
use flake "nixpkgs#hello"
For better flakes support: nix-direnv
Version Managers
# rbenv
use rbenv
# Node.js (fuzzy matching)
use node 18
use node 18.17.0
# From .nvmrc
use node
Validation
# Require variables
env_vars_required API_KEY DATABASE_URL
# Watch for file changes
watch_file package.json
watch_file config/*.yaml
watch_dir config
# Check git branch
if on_git_branch main; then
export DEPLOY_ENV=production
fi
# Minimum version
direnv_version 2.32.0
# Strict mode (exit on errors)
strict_env
Recommended .envrc Template
#!/usr/bin/env bash
# .envrc - Project environment configuration
# Enforce direnv version
direnv_version 2.32.0
# Load .env file if exists
dotenv_if_exists
# Load local overrides (not committed)
source_env_if_exists .envrc.local
# Language-specific layout
layout node
# or: layout python3
# Add project paths
PATH_add bin
PATH_add scripts
# Development settings
export NODE_ENV=development
export LOG_LEVEL=debug
# Watch configuration files
watch_file package.json
watch_file .tool-versions
Project Structure Best Practice
my-project/
├── .envrc # Development environment (committed)
├── .envrc.local # Local overrides (gitignored)
├── .env # Environment variables (gitignored)
├── .env.example # Template for team members (committed)
└── .direnv/ # Cache directory (gitignored)
.gitignore
# Environment files with secrets
.env
.env.local
.envrc.local
# direnv cache
.direnv/
Layered Configuration
Use parent directory inheritance:
# ~/projects/.envrc (global dev settings)
export EDITOR=vim
export PAGER=less
# ~/projects/api/.envrc
source_up # Inherit from parent
export API_PORT=3000
layout node
# ~/projects/api/feature-x/.envrc
source_up # Inherit from api
export FEATURE_FLAG=true
Custom Extensions
Create at ~/.config/direnv/direnvrc:
# ~/.config/direnv/direnvrc
# Kubernetes context switcher
use_kubernetes() {
local context="${1:-default}"
export KUBECONFIG="${HOME}/.kube/config"
kubectl config use-context "$context" >/dev/null 2>&1
log_status "kubernetes context: $context"
}
# AWS profile switcher
use_aws() {
local profile="${1:-default}"
export AWS_PROFILE="$profile"
log_status "aws profile: $profile"
}
# Azure subscription
use_azure() {
local subscription="$1"
export AZURE_SUBSCRIPTION="$subscription"
az account set --subscription "$subscription" >/dev/null 2>&1
log_status "azure subscription: $subscription"
}
# uv Python layout (modern alternative)
layout_uv() {
if ! has uv; then
log_error "uv not found. Install: curl -LsSf https://astral.sh/uv/install.sh | sh"
return 1
fi
VIRTUAL_ENV="$(pwd)/.venv"
if [[ ! -d "$VIRTUAL_ENV" ]]; then
uv venv
fi
PATH_add "$VIRTUAL_ENV/bin"
export VIRTUAL_ENV
}
Usage in .envrc:
use kubernetes dev-cluster
use aws production
layout uv
Common Patterns
Python with uv
# .envrc
direnv_version 2.32.0
dotenv_if_exists
# Use uv for Python
if has uv; then
layout_uv
else
layout python3
fi
export PYTHONDONTWRITEBYTECODE=1
Node.js Project
# .envrc
direnv_version 2.32.0
dotenv_if_exists
source_env_if_exists .envrc.local
layout node
export NODE_ENV=development
export PORT=3000
watch_file package.json
Kubernetes Development
# .envrc
dotenv_if_exists
use_kubernetes dev-cluster
export KUBECONFIG="${HOME}/.kube/config"
export KUBE_NAMESPACE=my-app
PATH_add bin
Multi-Environment Support
# .envrc
direnv_version 2.32.0
# Determine environment
if on_git_branch main; then
ENV=production
elif on_git_branch staging; then
ENV=staging
else
ENV=development
fi
export APP_ENV="$ENV"
# Load environment-specific config
dotenv_if_exists ".env.$ENV"
source_env_if_exists ".envrc.$ENV"
log_status "environment: $ENV"
Troubleshooting Quick Reference
# Check status
direnv status
# Force reload
direnv reload
# Re-allow .envrc
direnv allow
# Debug environment
direnv dump
direnv show_dump
# Export for debugging
direnv export bash
Common Issues
| Issue | Solution |
|---|---|
| Environment not loading | Run direnv allow |
| Hook not working | Verify hook in shell config, restart shell |
| Slow loading | Use nix-direnv for Nix; reduce watch_file calls |
| Variables not unloading | Check for export -f functions; restart shell |
IDE Integration
VS Code
Install direnv extension.
JetBrains IDEs
Install direnv integration plugin.
Emacs
(use-package envrc
:hook (after-init . envrc-global-mode))
Vim/Neovim
" Use direnv.vim plugin
Plug 'direnv/direnv.vim'
Security Notes
- Always review .envrc before allowing - direnv executes arbitrary code
- Never commit secrets - Use .env files in .gitignore
- Use .envrc.local for sensitive overrides - Keep local, never commit
- Trust hierarchy - Parent directories can override child settings
References
references/installation.md- Complete installation guidereferences/stdlib-functions.md- Full stdlib referencereferences/troubleshooting.md- Extended troubleshooting- Official docs: https://direnv.net/
- Stdlib reference: https://direnv.net/man/direnv-stdlib.1.html
- nix-direnv: https://github.com/nix-community/nix-direnv