Streamlining Environment Variables with direnv: A Developer's Guide

Streamlining Environment Variables with direnv: A Developer's Guide

March 13, 2025

4 min read

Streamlining Environment Variables with direnv: A Developer's Guide
Watch on YouTube

The Challenge of Environment Variables

Almost every project requires local environment variables - whether for running code, deploying applications, managing infrastructure, or executing automation scripts. Managing these variables efficiently can significantly improve your workflow and prevent common configuration headaches. In this post, I'll share practical approaches to handling environment variables more effectively. All code examples referenced here are available in the RadzionKit repository.

The Traditional Approach and Its Limitations

When working with environment-dependent code, you might typically create a script like set_env_vars.sh to define the necessary variables for your project. However, this approach creates friction - you need to remember to run this script every time you open a new terminal session. A more seamless solution exists in direnv, a tool that automatically loads environment variables specific to your current directory. With direnv, your environment is properly configured the moment you navigate to your project folder, eliminating the manual step entirely.

Using direnv: Security First

When first navigating to a directory with an .envrc file, direnv will not immediately load the environment. Instead, it displays an error message prompting you to run direnv allow. This security mechanism prevents potentially harmful scripts from automatically executing. Similarly, whenever you modify the .envrc file, direnv requires re-authorization through the same command.

running direnv allow
running direnv allow

Backing Up Your Environment Configuration

While .envrc files are great for local development, you'll need a way to back up your environment variables when sharing the project with others or setting up on a new machine. The following bash script helps you create a backup by finding all .envrc files in your project and saving their contents to a single text file.

#!/bin/bash

# Get the root folder name
root_name=$(basename "$(pwd)")

# Create output file name
output_file="${root_name}_env_vars.txt"

# Find all .envrc files and create a formatted output
output=""
while IFS= read -r file; do
    # Get relative path
    rel_path=$(dirname "$file")

    # Add path header
    output+="$rel_path\n\n"

    # Add file content
    output+="$(cat "$file")\n\n"
done < <(find . -name ".envrc" -type f)

# Write to file
echo -e "$output" > "$output_file"

echo "All .envrc files have been saved to $output_file"

After running this script, you'll have a single text file containing all your environment configurations. Store this backup in a secure location such as Google Drive, Dropbox, or as a locked note in your password manager or Notes app.

Restoring Your Environment Configuration

When you need to restore your environment variables on a new machine or after a system reset, you'll need a complementary script to recreate the .envrc files from your backup. The following script does exactly that - it reads from your clipboard (where you've pasted the contents of your backup file) and recreates all the .envrc files in their original locations. This approach makes environment restoration as simple as copying your backup text and running a single command.

#!/bin/bash

# Create temporary file for clipboard content
temp_file=$(mktemp)

# Get clipboard content based on OS
if [[ "$OSTYPE" == "darwin"* ]]; then
    pbpaste > "$temp_file"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
    xclip -selection clipboard -o > "$temp_file"
else
    echo "Unsupported operating system"
    rm "$temp_file"
    exit 1
fi

# Process the clipboard content
current_file=""
current_content=""

while IFS= read -r line; do
    # Skip empty lines and comments
    if [[ -z "$line" || "$line" =~ ^# ]]; then
        # If it's a comment, add it to current content
        if [[ "$line" =~ ^# ]]; then
            current_content+="$line\n"
        fi
        continue
    fi

    # If line doesn't start with 'export', it's a path
    if [[ ! "$line" =~ ^export ]]; then
        # If we have content from previous file, write it
        if [[ -n "$current_file" && -n "$current_content" ]]; then
            echo "Creating $current_file"
            mkdir -p "$(dirname "$current_file")"
            echo -e "$current_content" > "$current_file"
            current_content=""
        fi

        # Set up new file
        line="${line#./}"
        current_file="$line/.envrc"
    else
        # Append export line to current content
        current_content+="$line\n"
    fi
done < "$temp_file"

# Write the last file if we have content
if [[ -n "$current_file" && -n "$current_content" ]]; then
    echo "Creating $current_file"
    mkdir -p "$(dirname "$current_file")"
    echo -e "$current_content" > "$current_file"
fi

# Clean up
rm "$temp_file"

echo "All .envrc files have been restored"

Conclusion

By integrating direnv into your workflow and using these backup and restoration scripts, you can eliminate the friction of managing environment variables across projects and machines. This approach not only saves time but also reduces configuration errors, allowing you to focus on what truly matters—building and shipping great software.