How to Manage Environment Variables in Dotfiles Securely: A Defence-in-Depth Guide

The most secure way to manage environment variables in dotfiles is to never commit secrets to version control, store only safe defaults in tracked files like zsh/config/env.zsh, and source personal secrets from an untracked local file such as ~/.zshrc.local.

Managing sensitive credentials alongside public configuration requires a careful balance between convenience and security. The issmirnov/dotfiles repository demonstrates a robust, defence-in-depth approach to handle environment variables that keeps secrets out of Git while maintaining a reproducible baseline across machines. This guide examines the specific patterns implemented in the source code to help you manage environment variables in dotfiles securely.

The Three-Layer Security Model

The repository implements a layered strategy that separates public configuration from private data, ensuring that sensitive values never appear in commit history.

Layer 1: Exclude Secrets from Version Control

The first line of defence is the top-level .gitignore file, which explicitly excludes any file matching *.env and similar dotenv patterns. This rule is reinforced in auxiliary locations: ubersicht/.gitignore and git/template/info/exclude both contain identical exclusions with explanatory comments. These multiple exclusion points guarantee that temporary or local environment files remain untracked even when generated by third-party tools.

Layer 2: Version-Control Safe Defaults

Non-sensitive variables that should be shared across all machines live in zsh/config/env.zsh. This file exports reproducible baseline settings such as LC_ALL, LANG, TERM, EDITOR, VISUAL, and XDG_CONFIG_HOME. Because these values contain no secrets, they can be safely committed and cloned to any workstation without risk of credential leakage.

Layer 3: Local Overrides for Private Data

The main Zsh entry point at zsh/zshrc implements conditional sourcing for machine-specific secrets. At line 66, the configuration checks for the existence of a local override file before sourcing it:

[[ -f ~/.zshrc.local ]] && source ~/.zshrc.local

Because ~/.zshrc.local resides outside the repository structure, it never appears in git status or diff outputs, making it the canonical location for API tokens, cloud credentials, and proprietary configuration.

Implementation Details from the Source Code

The issmirnov/dotfiles repository separates concerns across specific files to maintain this security boundary.

Safe, tracked variables in zsh/config/env.zsh:


# Locale & terminal

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export TERM=xterm-256color

# Preferred editors

export EDITOR=vim
export VISUAL=vim

# XDG base directory

export XDG_CONFIG_HOME="$HOME/.config"

Untracked secrets in ~/.zshrc.local (create this manually):


# Example: AWS credentials (never commit!)

export AWS_ACCESS_KEY_ID="AKIA…"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

# Example: GitHub token for CLI tools

export GH_TOKEN="ghp_XXXXXXXXXXXXXXXXXXXXXXXX"

The conditional check in zsh/zshrc ensures that shell initialization continues normally even if the local file is absent, preventing errors on fresh installs or CI environments.

Step-by-Step Workflow for Adding Secrets

Follow this exact pattern to manage environment variables in dotfiles securely when adding new credentials:

  1. Create or edit your local override file:
nano ~/.zshrc.local
  1. Add your sensitive exports using standard shell syntax:
export MY_API_KEY="secret_value_here"
  1. Verify the file remains untracked:
git status

The output should not list .zshrc.local or any .env files, confirming that .gitignore rules are active.

Auditing Your Configuration

To verify that your exclusion rules are working correctly before attempting to commit sensitive data, use Git's built-in ignore checker:

git check-ignore -v .env

A properly configured repository will return the matching pattern and file location, such as:


.gitignore:3:*.env    .env

This confirmation step provides assurance that files matching the pattern are definitively excluded from the index.

Summary

  • Never commit raw secrets: The .gitignore file at the repository root excludes *.env patterns, while git/template/info/exclude and ubersicht/.gitignore provide redundant protection.
  • Store safe defaults in tracked files: Use zsh/config/env.zsh for locale, terminal, and editor variables that can be shared across all machines.
  • Source private data locally: The zsh/zshrc file conditionally sources ~/.zshrc.local, creating a secure boundary for machine-specific credentials.
  • Verify exclusions: Use git check-ignore to audit patterns before adding sensitive files.

Frequently Asked Questions

What happens if I accidentally commit a .env file to my dotfiles repository?

If a secret is committed, immediately rotate the compromised credentials and use git filter-repo or BFG Repo-Cleaner to remove the file from history. After cleanup, add the file name to .gitignore and verify with git check-ignore to prevent recurrence. The issmirnov/dotfiles repository prevents this scenario by excluding .env patterns before any commits are made.

Can I use this pattern with Bash instead of Zsh?

Yes. The conditional sourcing pattern [[ -f ~/.bashrc.local ]] && source ~/.bashrc.local works identically in Bash. Place this check at the end of your ~/.bashrc file, and store your secrets in ~/.bashrc.local, which should be listed in .gitignore. The security model remains the same across POSIX-compliant shells.

How do I migrate existing secrets out of my dotfiles repository?

First, move all sensitive export statements from your tracked configuration files into a new ~/.zshrc.local file. Then run git rm --cached on any files that previously contained secrets to remove them from the index while preserving local copies. Finally, add those file patterns to .gitignore and commit the changes. Verify with git log --full-history -- <filename> to ensure secrets no longer appear in the history.

Should I encrypt my ~/.zshrc.local file for additional security?

While the local file approach keeps secrets out of version control, encrypting ~/.zshrc.local adds protection against physical machine compromise or backup theft. Tools like git-crypt, sops, or age can encrypt the file at rest, requiring decryption at shell startup. However, ensure the decryption key is not stored in the dotfiles repository itself, or you reintroduce the original security risk.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →