
Direnv
- 109 installs
- 6 repo stars
- Updated July 22, 2026
- julianobarbosa/claude-code-skills
Manage environment variables and project-specific configurations automatically.
About
Guide for using direnv - a shell extension for loading directory-specific environment variables.
- Installing and configuring direnv on macOS or Linux
- Creating or modifying .envrc files for projects
Direnv by the numbers
- 109 all-time installs (skills.sh)
- +2 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #532 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianobarbosa/claude-code-skills --skill direnvAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 109 |
|---|---|
| repo stars | ★ 6 |
| Last updated | July 22, 2026 |
| Repository | julianobarbosa/claude-code-skills ↗ |
What it does
Manage environment variables and project-specific configurations automatically.
Files
direnv Skill
This skill provides comprehensive guidance for working with direnv, covering installation, configuration, stdlib functions, and best practices for per-project environment management.
When to Use This Skill
Use this skill when:
- Installing and configuring direnv on macOS or Linux
- Creating or modifying
.envrcfiles for projects - Setting up per-project environment variables
- Configuring language-specific layouts (Python, Node.js, Ruby, Go, Perl)
- Integrating direnv with Nix or Nix Flakes
- Managing secrets and environment configuration for teams
- Troubleshooting environment loading issues
- Creating custom direnv extensions
Core Concepts
What is direnv?
direnv is a shell extension that loads and unloads environment variables based on the current directory. When you cd into a directory with a .envrc file, direnv automatically loads the environment. When you leave, it unloads the changes.
Security Model
direnv uses an allowlist-based security approach:
- New or modified
.envrcfiles must be explicitly allowed withdirenv allow - Prevents automatic execution of untrusted scripts
- Use
direnv denyto revoke access
How It Works
1. Shell hook intercepts directory changes 2. Checks for .envrc file in current or parent directories 3. If allowed, executes .envrc in a bash subshell 4. Captures exported variables and applies them to current shell
Installation
macOS (Homebrew - Recommended)
brew install direnvLinux
# Ubuntu/Debian
sudo apt install direnv
# Fedora
sudo dnf install direnv
# Arch
sudo pacman -S direnv
# Binary installer (any system)
curl -sfL https://direnv.net/install.sh | bashVerify Installation
direnv versionShell Configuration
Add the hook to your shell's config file. This is required for direnv to function.
Zsh (~/.zshrc)
eval "$(direnv hook zsh)"With Oh My Zsh:
plugins=(... direnv)Bash (~/.bashrc)
eval "$(direnv hook bash)"Important: Place after rvm, git-prompt, and other prompt-modifying extensions.
Fish (~/.config/fish/config.fish)
direnv hook fish | sourceAfter Configuration
Restart your shell:
exec $SHELL.envrc File Basics
Creating an .envrc
# In your project directory
touch .envrc
# Edit with your preferred editor
vim .envrcBasic Syntax
# Export environment variables
export NODE_ENV=development
export API_URL=http://localhost:3000
export DATABASE_URL=postgres://localhost/myapp
# The export keyword is required for direnv to capture variablesAllowing the .envrc
# Allow current directory
direnv allow
# Allow specific path
direnv allow /path/to/project
# Deny/revoke access
direnv denyStandard Library Functions
direnv includes a powerful stdlib. Always prefer stdlib functions over manual exports.
PATH Management
# Prepend to PATH (safer than manual export)
PATH_add bin
PATH_add node_modules/.bin
PATH_add scripts
# Add to arbitrary path-like variable
path_add PYTHONPATH lib
path_add LD_LIBRARY_PATH /opt/lib
# Remove from PATH
PATH_rm "*/.git/bin"Environment File Loading
# Load .env file (current directory)
dotenv
# Load specific file
dotenv .env.local
# Load only if exists (no error)
dotenv_if_exists .env.local
dotenv_if_exists .env.${USER}
# Source another .envrc
source_env ../.envrc
source_env /path/to/.envrc
# Search upward and source parent .envrc
source_up
# Source if exists
source_env_if_exists .envrc.localLanguage Layouts
Node.js:
# Adds node_modules/.bin to PATH
layout nodePython:
# Creates virtualenv in .direnv/python-X.X/
layout python
# Use specific Python version
layout python python3.11
# Shortcut for Python 3
layout python3
# Use Pipenv (reads from Pipfile)
layout pipenvRuby:
# Sets GEM_HOME to project directory
layout rubyGo:
# Modifies GOPATH and adds bin to PATH
layout goPerl:
# Configures local::lib environment
layout perlNix Integration
# Load nix-shell environment
use nix
# With specific file
use nix shell.nix
# Load from Nix flake
use flake
# Load specific flake
use flake "nixpkgs#hello"
use flake ".#devShell"For better Nix Flakes support, install nix-direnv:
# Provides faster, cached use_flake implementation
# https://github.com/nix-community/nix-direnvVersion Managers
# rbenv
use rbenv
# Node.js (with fuzzy version matching)
use node 18
use node 18.17.0
# Reads from .nvmrc if version not specified
use node
# Julia
use julia 1.9Validation
# Require environment variables (errors if missing)
env_vars_required API_KEY DATABASE_URL SECRET_KEY
# Enforce minimum direnv version
direnv_version 2.32.0
# Check git branch
if on_git_branch main; then
export DEPLOY_ENV=production
fi
if on_git_branch develop; then
export DEPLOY_ENV=staging
fiFile Watching
# Reload when files change
watch_file package.json
watch_file requirements.txt
watch_file .tool-versions
watch_file config/*.yaml
# Watch entire directory
watch_dir config
watch_dir migrationsUtility Functions
# Check if command exists
if has docker; then
export DOCKER_HOST=unix:///var/run/docker.sock
fi
# Expand relative path to absolute
expand_path ./bin
# Find file searching upward
find_up package.json
# Enable strict mode (exit on errors)
strict_env
# Load prefix (configures CPATH, LD_LIBRARY_PATH, etc.)
load_prefix /usr/local/custom
# Load remote script with integrity verification
source_url https://example.com/script.sh "sha256-HASH..."Best Practices
Recommended .envrc Template
#!/usr/bin/env bash
# .envrc - Project environment configuration
# Enforce direnv version for team consistency
direnv_version 2.32.0
# Load .env if exists
dotenv_if_exists
# Load local overrides (not committed to git)
source_env_if_exists .envrc.local
# Language-specific layout
layout node # or: layout python3
# Add project bin directories
PATH_add bin
PATH_add scripts
# Development defaults
export NODE_ENV="${NODE_ENV:-development}"
export LOG_LEVEL="${LOG_LEVEL:-debug}"
# Watch for dependency changes
watch_file package.json
watch_file .nvmrcGit Configuration
.gitignore:
# Environment files with secrets
.env
.env.local
.envrc.local
# direnv virtualenv/cache
.direnv/Commit to repository:
.envrc(base configuration, no secrets).env.example(template for team members)
Secrets Management
Never commit secrets. Use environment variable fallbacks:
# .envrc (committed)
export DATABASE_URL="${DATABASE_URL:-postgres://localhost/dev}"
export API_KEY="${API_KEY:-}"
# Validate required secrets
env_vars_required API_KEY
# .envrc.local (gitignored)
export DATABASE_URL="postgres://user:secret@prod/app"
export API_KEY="actual-secret-key"Layered Configuration
# ~/projects/.envrc (global dev settings)
export EDITOR=vim
# ~/projects/api/.envrc
source_up
export API_PORT=3000
# ~/projects/api/feature/.envrc
source_up
export FEATURE_FLAG=trueProject Structure
my-project/
├── .envrc # Base environment (committed)
├── .envrc.local # Local overrides (gitignored)
├── .env # Environment variables (gitignored)
├── .env.example # Template for team (committed)
└── .direnv/ # direnv cache (gitignored)Custom Extensions
Create ~/.config/direnv/direnvrc for custom functions:
#!/usr/bin/env bash
# ~/.config/direnv/direnvrc
# Custom function: Use specific Kubernetes context
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"
}
# Custom function: Load from AWS Secrets Manager
use_aws_secrets() {
local secret_name="$1"
local region="${2:-us-east-1}"
eval "$(aws secretsmanager get-secret-value \
--secret-id "$secret_name" \
--region "$region" \
--query SecretString \
--output text | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""')"
log_status "loaded secrets from: $secret_name"
}
# Custom function: Use asdf versions from .tool-versions
use_asdf() {
watch_file .tool-versions
source_env "$(asdf direnv local)"
}Usage in .envrc:
use kubernetes dev-cluster
use aws_secrets myapp/dev
use asdfCommands Reference
| Command | Description |
|---|---|
direnv allow | Allow the current .envrc |
direnv deny | Revoke .envrc access |
direnv reload | Force reload environment |
direnv status | Show current status |
direnv dump | Dump current environment |
direnv edit | Open .envrc in editor |
direnv version | Show direnv version |
Troubleshooting
Environment Not Loading
# Check status
direnv status
# Force reload
direnv reload
# Re-allow .envrc
direnv allow
# Check if hook is installed
echo $DIRENV_DIRShell Hook Issues
1. Verify hook is in shell config file 2. Ensure it's at the END of the file 3. Restart shell completely: exec $SHELL 4. Check for errors: direnv hook zsh
Performance Issues
# Show what's being evaluated
direnv show_dump
# For Nix, use nix-direnv for caching
# https://github.com/nix-community/nix-direnvDebugging
# Verbose output
export DIRENV_LOG_FORMAT='%s'
# Show exported variables
direnv dump | jq
# Test .envrc syntax
bash -n .envrcIDE Integration
VS Code
Install direnv extension for automatic environment loading in integrated terminal.
JetBrains
Install direnv integration plugin.
Neovim
Use direnv.vim or configure with lua.
Common Patterns
Development vs Production
# .envrc
export NODE_ENV="${NODE_ENV:-development}"
if [[ "$NODE_ENV" == "development" ]]; then
export DEBUG=true
export LOG_LEVEL=debug
else
export DEBUG=false
export LOG_LEVEL=info
fiMulti-Service Projects (Monorepo)
# root/.envrc
export PROJECT_ROOT="$(pwd)"
export COMPOSE_PROJECT_NAME=myapp
# services/api/.envrc
source_up
export SERVICE_NAME=api
export SERVICE_PORT=3000
# services/web/.envrc
source_up
export SERVICE_NAME=web
export SERVICE_PORT=8080Docker Integration
# .envrc
export COMPOSE_FILE=docker-compose.yml
export COMPOSE_PROJECT_NAME="${PWD##*/}"
if has docker-compose; then
export DOCKER_HOST="${DOCKER_HOST:-unix:///var/run/docker.sock}"
fi
# Add Docker bin for containers that install CLI tools
PATH_add .docker/binReferences
- Official Documentation
- Installation Guide
- Shell Hook Setup
- Standard Library Reference
- nix-direnv
- Homebrew Formula
---
Gotchas
- `direnv` reloads on `cd` but NOT on `.envrc` edit unless you re-enter the dir: A change to
.envrclooks applied (no error) but isn't untilcd .ordirenv reload. Usewatch_fileon.envrcitself if editing in-place. - `direnv allow` is keyed on file content hash, not path: Renaming
.envrc.devto.envrcand back keeps allow state. But editing a single byte revokes — even a stray trailing newline from saving in a new editor. - `layout python` creates `.direnv/python-X.Y/` tied to the host's python version: A python upgrade silently breaks the venv. Pin via
layout python python3.11or rebuild withrm -rf .direnv && direnv reload. - `source_up` searches ancestors, not just immediate parent: A stray
.envrcin~/or~/Projects/leaks into every subdirectory project. Audit withdirenv statusfrom deep in a tree to see all chained files. - Hook placement order matters in `.zshrc`: Place
eval "$(direnv hook zsh)"AFTER prompt/p10k setup. Earlier andprecmdhooks fire before prompt is ready — direnv output gets eaten by the prompt redraw. - `PATH_add bin` adds project-relative path that breaks when scripts `cd` elsewhere: The PATH entry is absolute (resolved at load), so
cd /tmp && project-binarystill works — but binaries that read$PWD/binat runtime do not.
direnv Installation Reference
Complete installation and shell configuration guide for all platforms.
Installation Methods
macOS
Homebrew (Recommended):
brew install direnvMacPorts:
sudo port install direnvNix:
nix-env -i direnvLinux
Debian/Ubuntu:
sudo apt update
sudo apt install direnvFedora:
sudo dnf install direnvArch Linux:
sudo pacman -S direnvAlpine:
apk add direnvNix:
nix-env -i direnvBinary Installer (Any Linux):
curl -sfL https://direnv.net/install.sh | bashFrom Source:
# Requires Go 1.16+
git clone https://github.com/direnv/direnv
cd direnv
make
sudo make installWindows
Scoop:
scoop install direnvChocolatey:
choco install direnvGit Bash/MSYS2:
pacman -S direnvWSL:
Use Linux installation methods.
Verify Installation
direnv version
# Expected: 2.32.0 or higherShell Hook Configuration
The shell hook is required for direnv to work. It must be added to your shell's configuration file.
Zsh
Add to ~/.zshrc:
eval "$(direnv hook zsh)"With Oh My Zsh:
Add direnv to your plugins array in ~/.zshrc:
plugins=(git docker direnv)With Prezto:
Enable the direnv module in ~/.zpreztorc:
zstyle ':prezto:load' pmodule \
'environment' \
'terminal' \
'editor' \
'history' \
'directory' \
'spectrum' \
'utility' \
'completion' \
'prompt' \
'direnv'Bash
Add to ~/.bashrc:
eval "$(direnv hook bash)"For macOS with bash, also add to~/.bash_profileif it sources~/.bashrc.
Fish
Add to ~/.config/fish/config.fish:
direnv hook fish | sourcePowerShell
Add to PowerShell profile ($PROFILE):
Invoke-Expression "$(direnv hook pwsh)"Elvish
Add to ~/.elvish/rc.elv:
eval (direnv hook elvish | slurp)Tcsh
Add to ~/.cshrc:
eval `direnv hook tcsh`Nushell
Add to $nu.config-path:
$env.config = ($env.config | merge {
hooks: {
pre_prompt: [{ ||
if (which direnv | is-empty) {
return
}
direnv export json | from json | default {} | load-env
}]
}
})POSIX Shell
Add to ~/.profile:
eval "$(direnv hook sh)"Applying Shell Configuration
After adding the hook, apply the changes:
# Zsh
source ~/.zshrc
# Bash
source ~/.bashrc
# Fish
source ~/.config/fish/config.fishOr simply restart your terminal.
Hook Placement Guidelines
1. Place at the end - The hook should be the last line in your config, after:
- Other shell extensions (rvm, nvm, pyenv)
- Prompt customizations
- Path modifications
2. After version managers - If using asdf, rbenv, pyenv, nvm:
# ~/.zshrc
# Version managers first
eval "$(pyenv init -)"
eval "$(rbenv init -)"
# direnv last
eval "$(direnv hook zsh)"Updating direnv
Homebrew
brew upgrade direnvSelf-update (binary install)
direnv version # Check current
curl -sfL https://direnv.net/install.sh | bash # Reinstall latestPackage Managers
# Debian/Ubuntu
sudo apt update && sudo apt upgrade direnv
# Fedora
sudo dnf upgrade direnv
# Arch
sudo pacman -Syu direnvGlobal Configuration
Create ~/.config/direnv/direnv.toml:
[global]
# Hide environment diff in output
hide_env_diff = false
# Load .env files
load_dotenv = true
# Warn when using old stdlib functions
warn_timeout = "5s"
# Disable loading .envrc globally (emergency)
# disable_stdin = true
[whitelist]
# Auto-allow specific paths (use with caution)
# prefix = ["/home/user/trusted-projects"]
# exact = ["/home/user/specific-project/.envrc"]Verification
After installation, verify everything works:
# 1. Check version
direnv version
# 2. Create test directory
mkdir /tmp/direnv-test && cd /tmp/direnv-test
# 3. Create .envrc
echo 'export TEST_VAR=hello' > .envrc
# 4. Allow it
direnv allow
# 5. Verify variable is set
echo $TEST_VAR
# Should output: hello
# 6. Leave directory and verify unloading
cd /tmp
echo $TEST_VAR
# Should be empty
# 7. Cleanup
rm -rf /tmp/direnv-testTroubleshooting Installation
Hook Not Working
1. Verify hook is in correct config file:
# Zsh
grep "direnv hook" ~/.zshrc
# Bash
grep "direnv hook" ~/.bashrc2. Source the config:
source ~/.zshrc # or ~/.bashrc3. Restart terminal completely
4. Check for conflicts:
# Look for duplicate hooks
grep -r "direnv" ~/.*rc ~/.*profile 2>/dev/nullPermission Denied
# Make direnv executable
chmod +x $(which direnv)Old Version Warnings
# Update to latest
brew upgrade direnv # macOS
sudo apt upgrade direnv # UbuntuPATH Issues
Ensure direnv is in PATH:
which direnv
# Should return path like /usr/local/bin/direnv
# If not found, add to PATH
export PATH="/usr/local/bin:$PATH" # Add to shell configUninstallation
Homebrew
brew uninstall direnvManual
# Remove binary
sudo rm $(which direnv)
# Remove configuration
rm -rf ~/.config/direnv
rm -rf ~/.local/share/direnv
# Remove hook from shell config (edit manually)Remember to remove the hook from your shell configuration files.
direnv Standard Library Reference
Complete reference for all direnv stdlib functions.
PATH Functions
PATH_add
Add directories to the beginning of PATH.
# Add single directory
PATH_add bin
# Add multiple directories
PATH_add bin scripts tools
# Add node_modules binaries
PATH_add node_modules/.bin
# Absolute path
PATH_add /opt/custom/binPATH_rm
Remove directories matching patterns from PATH.
# Remove by pattern (glob)
PATH_rm "*/.git/bin"
# Remove specific path
PATH_rm "/old/path/bin"path_add (lowercase)
Synonym for PATH_add.
path_add binMANPATH_add
Add directories to MANPATH.
MANPATH_add man
MANPATH_add /opt/custom/share/manEnvironment File Functions
dotenv
Load a .env file into the environment.
# Load .env from current directory
dotenv
# Load specific file
dotenv .env.local
# Load with path
dotenv config/.env
# Multiple files (loaded in order)
dotenv .env
dotenv .env.local.env format:
# Comments are supported
KEY=value
QUOTED="value with spaces"
MULTILINE="line1\nline2"
EMPTY=dotenv_if_exists
Load .env file only if it exists (no error if missing).
# Safe loading
dotenv_if_exists .env.local
dotenv_if_exists .env.${APP_ENV}source_env
Source another .envrc file.
# Source specific file
source_env ../.envrc
source_env /path/to/.envrc
# Source relative path
source_env ../shared/.envrcsource_env_if_exists
Source .envrc only if it exists.
source_env_if_exists .envrc.local
source_env_if_exists ../.envrcsource_up
Load .envrc from parent directories (searches upward).
# Load first .envrc found in parent directories
source_up
# Specify maximum depth (default: unlimited)
source_up 2source_up_if_exists
Load parent .envrc if found, no error if not found.
source_up_if_existssource_url
Download and source a script from URL with hash verification.
# Source with SHA256 verification
source_url "https://example.com/script.sh" "sha256-abc123..."Layout Functions
Layouts configure the environment for specific programming languages.
layout python / layout python3
Create and activate a Python virtual environment.
# Use default Python
layout python
# Use Python 3 explicitly
layout python3
# Use specific version
layout python python3.11
# Use specific interpreter path
layout python /usr/local/bin/python3.12What it does:
- Creates virtualenv in
.direnv/python-<version> - Adds virtualenv to PATH
- Sets VIRTUAL_ENV environment variable
layout pipenv
Use Pipenv for virtual environment management.
layout pipenvWhat it does:
- Uses
pipenv --venvto locate virtualenv - Activates the Pipenv environment
layout poetry
Use Poetry for virtual environment management.
layout poetrylayout node
Configure Node.js environment.
layout nodeWhat it does:
- Adds
node_modules/.binto PATH - Sets NPM_CONFIG_PREFIX
layout ruby
Configure Ruby environment with local gem installation.
layout rubyWhat it does:
- Sets GEM_HOME to
.direnv/ruby - Adds gem bin directory to PATH
layout go
Configure Go environment.
layout goWhat it does:
- Sets GOPATH to current directory
- Adds
$GOPATH/binto PATH
layout perl
Configure Perl local::lib environment.
layout perlWhat it does:
- Sets up local::lib in
.direnv/perl5 - Configures PERL5LIB and PATH
layout julia
Configure Julia depot path.
layout julialayout r
Configure R library paths.
layout rlayout anaconda / layout miniconda
Activate Anaconda/Miniconda environment.
# Use environment by name
layout anaconda myenv
# Use environment by path
layout anaconda /path/to/envNix Functions
use nix
Load environment from shell.nix or default.nix.
# Load from shell.nix
use nix
# Load from specific file
use nix -p python3 nodejsuse flake
Load environment from a Nix flake.
# Load default devShell
use flake
# Load specific output
use flake ".#devShells.x86_64-linux.default"
# Load from nixpkgs
use flake "nixpkgs#hello"For performance, use nix-direnv.
Version Manager Functions
use asdf
Load asdf version manager.
use asdfuse node
Activate Node.js version (requires fnm, nodenv, or nvm).
# Use specific version (fuzzy matching)
use node 18
use node 18.17.0
use node lts
# Use version from .nvmrc
use node
# Use version from .node-version
use nodeuse rbenv
Activate rbenv Ruby version.
use rbenvuse pyenv
Activate pyenv Python version.
use pyenvuse volta
Activate Volta Node.js version.
use voltaUtility Functions
has
Check if a command exists.
if has docker; then
export DOCKER_HOST="unix:///var/run/docker.sock"
fi
if has uv; then
layout_uv
else
layout python3
fiexpand_path
Expand relative path to absolute.
MYPATH=$(expand_path ./config)
export CONFIG_DIR="$MYPATH"find_up
Find a file by searching parent directories.
# Find package.json in current or parent directories
PACKAGE_JSON=$(find_up package.json)
if [[ -n "$PACKAGE_JSON" ]]; then
export PROJECT_ROOT="$(dirname "$PACKAGE_JSON")"
fiuser_rel_path
Convert absolute path to user-relative path.
# /home/user/projects -> ~/projects
SHORT_PATH=$(user_rel_path "$PWD")realpath.dirname / realpath.basename
Get directory name or base name of real path.
DIR=$(realpath.dirname "$PWD/.envrc")
NAME=$(realpath.basename "$PWD")Watch Functions
watch_file
Trigger reload when file changes.
# Watch single file
watch_file package.json
# Watch multiple files
watch_file package.json package-lock.json
# Watch with glob
watch_file config/*.yaml
watch_file .tool-versionswatch_dir
Watch directory for any changes (recursive).
watch_dir config
watch_dir src/templatesValidation Functions
env_vars_required
Fail if required environment variables are not set.
# Require single variable
env_vars_required API_KEY
# Require multiple variables
env_vars_required API_KEY DATABASE_URL REDIS_URLdirenv_version
Require minimum direnv version.
# Require at least 2.32.0
direnv_version 2.32.0Logging Functions
log_status
Log informational message.
log_status "using python $(python --version)"
log_status "kubernetes context: $(kubectl config current-context)"log_error
Log error message to stderr.
log_error "API_KEY not set"Git Functions
on_git_branch
Check if on specific git branch.
if on_git_branch main; then
export DEPLOY_ENV=production
elif on_git_branch staging; then
export DEPLOY_ENV=staging
else
export DEPLOY_ENV=development
fi
# Multiple branches
if on_git_branch main master; then
export IS_DEFAULT_BRANCH=true
fiControl Functions
strict_env
Enable strict mode (exit on undefined variables and errors).
strict_env
# After this, unset variable access will error
echo "$UNDEFINED_VAR" # Will failunstrict_env
Disable strict mode.
unstrict_envload_prefix
Configure environment for software installed in custom prefix.
# Load /opt/myapp/bin, /opt/myapp/lib, etc.
load_prefix /opt/myapp
# Adds to PATH, LD_LIBRARY_PATH, PKG_CONFIG_PATH, etc.semver_search
Find files matching semantic version pattern.
# Find python3.X binaries
PYTHON=$(semver_search /usr/bin "python" "3")Environment Export
export_function
Export a bash function (use sparingly).
my_helper() {
echo "helper function"
}
export_function my_helpersetenv
Set environment variable (alias for export).
setenv MY_VAR "value"Advanced Functions
fetchurl
Download file with caching.
# Download and cache
SCRIPT=$(fetchurl "https://example.com/script.sh" "sha256-...")
source "$SCRIPT"direnv_load
Load environment from subshell.
# Load from nix-shell
direnv_load nix-shell --run "direnv dump"
# Load from Docker
direnv_load docker run --rm myimage printenvCreating Custom Functions
Add to ~/.config/direnv/direnvrc:
# Custom layout for uv (modern Python)
layout_uv() {
local venv=".venv"
if ! has uv; then
log_error "uv not found"
return 1
fi
if [[ ! -d "$venv" ]]; then
log_status "creating venv with uv"
uv venv
fi
VIRTUAL_ENV="$PWD/$venv"
PATH_add "$VIRTUAL_ENV/bin"
export VIRTUAL_ENV
log_status "using uv virtualenv"
}
# AWS profile switcher
use_aws() {
local profile="${1:-default}"
export AWS_PROFILE="$profile"
log_status "aws profile: $profile"
}
# Kubernetes context
use_kubectl() {
local context="${1:-}"
if [[ -n "$context" ]]; then
kubectl config use-context "$context" >/dev/null 2>&1
log_status "kubectl context: $context"
fi
}
# Azure subscription
use_azure() {
local subscription="${1:-}"
if [[ -n "$subscription" ]]; then
az account set --subscription "$subscription" >/dev/null 2>&1
export AZURE_SUBSCRIPTION="$subscription"
log_status "azure subscription: $subscription"
fi
}
# Load secrets from 1Password
use_1password() {
local vault="${1:-Personal}"
local item="${2:-}"
if ! has op; then
log_error "1Password CLI not found"
return 1
fi
if [[ -n "$item" ]]; then
eval "$(op item get "$item" --vault "$vault" --format env)"
log_status "loaded secrets from 1password: $item"
fi
}Usage:
# .envrc
layout uv
use_aws production
use_kubectl dev-cluster
use_azure my-subscriptiondirenv Troubleshooting Reference
Comprehensive troubleshooting guide for common direnv issues.
Diagnostic Commands
# Check direnv status
direnv status
# Show current environment dump
direnv dump
# Show human-readable diff
direnv show_dump
# Export in shell format
direnv export bash
direnv export zsh
direnv export fish
# Force reload
direnv reload
# Check version
direnv version
# Edit .envrc with $EDITOR
direnv editCommon Issues
Issue: Environment Not Loading
Symptoms:
- Variables not set after entering directory
- No direnv output on
cd
Diagnostic:
cd /path/to/project
direnv statusSolutions:
1. Allow the .envrc:
direnv allow2. Verify hook is installed:
# Zsh
grep "direnv hook" ~/.zshrc
# Bash
grep "direnv hook" ~/.bashrc3. Source shell config:
source ~/.zshrc # or ~/.bashrc4. Restart terminal completely
5. Force reload:
direnv reload---
Issue: Shell Hook Not Working
Symptoms:
direnv: command not found- No environment changes on
cd
Solutions:
1. Check if direnv is in PATH:
which direnv
type direnv2. Add hook to correct file:
| Shell | File |
|---|---|
| Zsh | ~/.zshrc |
| Bash | ~/.bashrc (Linux) or ~/.bash_profile (macOS) |
| Fish | ~/.config/fish/config.fish |
3. Hook placement - must be at END of file:
# ~/.zshrc
# ... other configuration ...
# pyenv, rbenv, nvm, etc.
# direnv hook LAST
eval "$(direnv hook zsh)"4. Interactive shell only:
For bash, ensure hook is in interactive config:
# ~/.bashrc
if [[ $- == *i* ]]; then
eval "$(direnv hook bash)"
fi---
Issue: .envrc Blocked / Not Trusted
Symptoms:
direnv: error /path/to/.envrc is blocked. Run `direnv allow` to approve its contentSolutions:
1. Review and allow:
cat .envrc # Review content
direnv allow2. Allow specific path:
direnv allow /path/to/project3. Auto-allow trusted paths (in ~/.config/direnv/direnv.toml):
[whitelist]
prefix = ["/home/user/trusted-projects"]Use with caution - security risk.
---
Issue: Slow Loading / Performance
Symptoms:
- Shell prompt delayed on
cd - High CPU usage
Diagnostic:
# Time the loading
time (cd /project && direnv export bash)Solutions:
1. Reduce watch_file calls:
# Instead of many watch_file
watch_file file1 file2 file3
# Use fewer watches
watch_file package.json2. Use nix-direnv for Nix:
Install nix-direnv:
# Caches nix-shell evaluation
nix-env -i nix-direnvThen in .envrc:
if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-..."
fi
use flake3. Minimize source_up calls:
# Only source_up if needed
source_up_if_exists4. Cache expensive operations:
# Cache in .direnv/
CACHE_DIR=".direnv/cache"
mkdir -p "$CACHE_DIR"
if [[ ! -f "$CACHE_DIR/expensive-result" ]]; then
expensive_command > "$CACHE_DIR/expensive-result"
fi
source "$CACHE_DIR/expensive-result"---
Issue: Variables Not Unloading
Symptoms:
- Environment variables persist after leaving directory
- Old values remain
Solutions:
1. Check for exported functions:
# Functions persist across direnv
# Avoid export_function when possible2. Restart shell:
exec $SHELL3. Clear direnv cache:
rm -rf .direnv/
direnv reload4. Verify unload:
cd /project
echo $MY_VAR # Should be set
cd /
echo $MY_VAR # Should be empty---
Issue: Layout Python Not Working
Symptoms:
- Virtual environment not created
- Wrong Python version
Diagnostic:
ls -la .direnv/
python --version
which pythonSolutions:
1. Clear and recreate:
rm -rf .direnv/
direnv reload2. Specify Python version:
layout python python3.113. Check Python availability:
which python3.11
python3.11 --version4. Use uv instead (modern):
# In ~/.config/direnv/direnvrc
layout_uv() {
if [[ ! -d .venv ]]; then
uv venv
fi
VIRTUAL_ENV="$PWD/.venv"
PATH_add "$VIRTUAL_ENV/bin"
export VIRTUAL_ENV
} # In .envrc
layout uv---
Issue: dotenv Not Loading
Symptoms:
- Variables from .env not available
dotenv: command not found
Solutions:
1. Check .env file exists:
ls -la .env2. Verify .env format:
# Valid format
KEY=value
QUOTED="value with spaces"
# Invalid (no export needed)
export KEY=value # Remove 'export'3. Use dotenv_if_exists:
dotenv_if_exists # No error if missing4. Check file permissions:
chmod 644 .env---
Issue: source_up Not Finding Parent
Symptoms:
- Parent .envrc not loaded
source_up: No ancestor .envrc found
Solutions:
1. Verify parent .envrc exists:
ls -la ../.envrc
ls -la ../../.envrc2. Use source_up_if_exists:
source_up_if_exists # Silent if not found3. Explicit source:
source_env ../.envrc
source_env_if_exists ../../.envrc---
Issue: Nix/Flake Errors
Symptoms:
use nixoruse flakefails- Very slow loading
Solutions:
1. Install nix-direnv:
nix-env -i nix-direnv2. Add to ~/.config/direnv/direnvrc:
source ~/.nix-profile/share/nix-direnv/direnvrc3. Or inline in .envrc:
if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" \
"sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8="
fi
use flake4. Enable flakes in nix.conf:
# ~/.config/nix/nix.conf
experimental-features = nix-command flakes---
Issue: IDE Not Picking Up Environment
Symptoms:
- VS Code terminal doesn't have variables
- IDE tools can't find dependencies
Solutions:
1. VS Code - Install extension:
Install direnv extension
2. JetBrains - Install plugin:
Install direnv integration
3. Open terminal from correct directory:
cd /project
code . # Open VS Code from project directory4. Restart IDE after direnv allow
---
Issue: Conflicting with Version Managers
Symptoms:
- rbenv, pyenv, nvm conflicts
- Wrong versions used
Solutions:
1. Hook order - direnv LAST:
# ~/.zshrc
# Version managers first
eval "$(pyenv init -)"
eval "$(rbenv init -)"
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# direnv hook LAST
eval "$(direnv hook zsh)"2. Use direnv's version manager functions:
# .envrc
use node 18
use rbenv
use pyenv3. Or set version files:
# .envrc
watch_file .node-version
watch_file .ruby-version
watch_file .python-version---
Debug Script
Save as debug-direnv.sh:
#!/bin/bash
set -e
echo "=== direnv Debug Info ==="
echo
echo "Version:"
direnv version
echo
echo "Status:"
direnv status
echo
echo "Shell:"
echo "$SHELL"
echo
echo "Hook in config:"
case "$SHELL" in
*zsh)
grep "direnv" ~/.zshrc 2>/dev/null || echo "Not found in ~/.zshrc"
;;
*bash)
grep "direnv" ~/.bashrc 2>/dev/null || echo "Not found in ~/.bashrc"
;;
*fish)
grep "direnv" ~/.config/fish/config.fish 2>/dev/null || echo "Not found"
;;
esac
echo
echo ".envrc content:"
if [[ -f .envrc ]]; then
cat .envrc
else
echo "No .envrc in current directory"
fi
echo
echo "direnv allow status:"
if [[ -f .envrc ]]; then
direnv status | grep -i allowed || echo "Not allowed"
fi
echo
echo ".direnv directory:"
ls -la .direnv/ 2>/dev/null || echo "No .direnv directory"
echo
echo "Environment variables from direnv:"
direnv export bash 2>/dev/null | head -20 || echo "No exports"Run:
chmod +x debug-direnv.sh
./debug-direnv.shGetting Help
1. Official documentation: <https://direnv.net/> 2. GitHub issues: <https://github.com/direnv/direnv/issues> 3. Man page: man direnv / man direnv-stdlib
Reset Everything
Nuclear option - reset all direnv state:
# Remove all allowed .envrc entries
direnv prune
# Clear cache
rm -rf ~/.local/share/direnv/
# Clear project cache
rm -rf .direnv/
# Restart shell
exec $SHELL
# Re-allow
cd /project
direnv allow