Skip to main content

Flexible and secure .env loader for Python projects with support for environment switching, nested project structures, and external configuration directories.

Project description

dotenv-loader

Flexible and secure .env loader for Python projects with support for environment switching, nested project structures, and external configuration directories.

📚 Table of Contents

📖 Overview

dotenv-loader (Git, PyPI) provides a flexible yet straightforward way to load environment variables from .env files, tailored specifically for Python applications, including Django, Flask, Celery, FastCGI, WSGI, CLI tools, and more. It supports hierarchical configuration, making it extremely convenient to manage multiple environments (development, testing, production) without manually switching .env files or cluttering code with environment-specific logic.

🚀 Historical Context

Managing environment-specific settings is crucial in modern software development. The excellent python-dotenv package provides robust and versatile .env file loading functionality, but it lacks flexible mechanisms for dynamically switching configurations across multiple deployment environments or nested project structures.

dotenv-loader is built upon python-dotenv, enhancing it specifically to address the following practical challenges:

  • Dynamically switch environments without modifying code or manually managing environment variables.
  • Support flexible directory structures, such as monorepos or Django sub-applications.
  • Provide clear, hierarchical separation of environment-specific configurations to improve clarity and reduce human error.

While dotenv-loader specializes in dynamic and hierarchical environment selection tailored for specific deployment scenarios, python-dotenv offers broader and more general-purpose capabilities. Users whose needs aren't fully met by dotenv-loader are strongly encouraged to leverage python-dotenv's comprehensive functionality directly.

✨ Features

  • Hierarchical and prioritized .env file search: dotenv-loader follows a clear and intuitive priority order:

    1. Explicit path provided via DOTENV environment variable.
    2. Configuration directory (DOTCONFIG_ROOT) with customizable subdirectories per project and environment stage (DOTSTAGE).
    3. Automatic fallback to .env file located directly in the project root.
  • Dynamic project and stage selection: Quickly switch configurations by setting the DOTPROJECT and DOTSTAGE environment variables, allowing effortless toggling between multiple environments or projects.

  • Customizable file names: Use custom .env filenames to further separate and manage your configurations.

🔒 Security and Best Practices

Storing .env files separately in a dedicated configuration directory (DOTCONFIG_ROOT) outside your project source tree is a secure and recommended best practice. This approach significantly reduces the risk of accidentally leaking sensitive information (such as API keys, database credentials, etc.) during backups, version control operations, or file transfers. By keeping secrets separate from your codebase, dotenv-loader helps enforce a clear boundary between configuration and code, enhancing security and compliance.

⚙️ Installation

pip install dotenv-loader

✅ Python Compatibility

This package is tested on the following Python versions via GitHub Actions:

Python version Status
3.9 ✅ Supported
3.10 ✅ Supported
3.11 ✅ Supported
3.12 ✅ Supported
3.13 ✅ Supported

Automated tests are run on every push and pull request to ensure consistent support.

🛠 Usage

Basic Usage

By default, dotenv-loader automatically identifies the .env file from the current project's root directory:

import dotenv_loader

dotenv_loader.load_env()

Advanced Usage

import dotenv_loader

# Load environment with custom default settings 
# Each parameter is optional and first four can be overridden by environment variables:
dotenv_loader.load_env(
    project='mega-project',          # - explicitly set project name
    stage='production',              # - explicitly set stage name
    dotenv='./dir/.env.test'         # - explicitly set .env file 
    config_root='~/custom-configs',  # - custom config directory
    steps_to_project_root=1,         # - how many directories up to look for project root
    default_env_filename='env',      # - change the default '.env' name to you name
    override=True                    # - whether to overwrite existing environment variables 
                                     #   already defined in os.environ. Use False to preserve 
                                     #   values already present (e.g. from OS or CI/CD), or 
                                     #   True to always prefer .env contents.
)

Environment Variables

You can control the behavior of dotenv-loader using the following environment variables:

DOTENV — Path to the .env file or directory.

  • If a full file path is given, it overrides all other options. If the file is not found, a FileNotFoundError is raised.
  • If a directory path is given, the loader will look for an environment file in that directory, based on default_env_filename and DOTSTAGE (or fallback stage).

Examples:

DOTENV=/home/user/.env.custom python manage.py
# Uses this exact file; raises an error if not found
 
DOTENV=~/myconfigs/myproject python manage.py
DOTSTAGE=prod
# Loads ~/myconfigs/myproject/.env.prod
 
DOTENV=~/configs/project python manage.py  # calling load_env(stage='local')
# Loads ~/configs/project/.env.local

DOTPROJECT — Quickly switch between project environments:

DOTPROJECT=test python manage.py
# Loads: ~/.config/python-projects/test/.env

DOTSTAGE — Select a configuration stage within a project (prod, staging, test):

DOTSTAGE=staging python manage.py
# Loads: ~/.config/python-projects/myproject/.env.staging

DOTCONFIG_ROOT — Override the default configuration root directory:

DOTCONFIG_ROOT=~/myconfigs python manage.py
# Loads: ~/myconfigs/myproject/.env

DOTVERBOSE — Print the resolved path of the loaded .env file to stdout:

DOTVERBOSE=1 python manage.py
# Output: Use DOTENV file from: /home/user/.config/python-projects/projectname/.env 

Typical Directory Structure

~/.config/python-projects/
└── myproject/
    ├── .env          # Default configuration (typically a symlink 
                     # to .env.prod)
     
    ├── .env.prod     # Production configuration. Use explicitly with:
                     # DOTSTAGE=prod python manage.py
     
    ├── .env.staging  # Staging configuration. Use explicitly with: 
                     # DOTSTAGE=staging python manage.py
     
    └── .env.test     # Testing configuration. Use explicitly with: 
                      # DOTSTAGE=test python manage.py

myproject/
└── manage.py  # By default, loads ~/.config/python-projects/myproject/.env
    .env       # Used only if no .env.* files are found in 
               # ~/.config/python-projects/myproject

🧽 .env Resolution Rules and Precedence

dotenv-loader uses a deterministic and secure resolution strategy when selecting the appropriate .env file to load. The logic ensures maximum flexibility while maintaining clarity and safety.

Resolution Rules

  1. Environment Variables Take Precedence

    The following environment variables override their corresponding load_env() arguments:

    • DOTENV overrides dotenv
    • DOTPROJECT overrides project
    • DOTSTAGE overrides stage
    • DOTCONFIG_ROOT overrides config_root
  2. Relative Paths Are Context-Aware

    • Paths defined in environment variables (e.g., DOTENV, DOTCONFIG_ROOT) are resolved relative to the current working directory (PWD, as seen with pwd).

    • Paths passed directly to load_env() (e.g., dotenv, config_root) are resolved relative to the calling script's location, adjusted by steps_to_project_root.

    For example, if manage.py is at ~/projects/proj1/app/manage.py and steps_to_project_root=1, then project_root is considered to be ~/projects/proj1.

  3. Project and Stage Names Must Be Basenames

    Both DOTPROJECT/project and DOTSTAGE/stage must not include slashes. They are treated strictly as simple names (i.e., Path(name).name).

  4. Highest Priority: Explicit .env Path or Directory

    If either DOTENV or dotenv is defined, it takes priority over all other resolution logic.

    • If the value is a full path to a file, that file is loaded directly. If it doesn't exist, a FileNotFoundError is raised and no fallback is attempted.
    • If the value is a directory path, the loader will only search within that directory, constructing the target filename as [default_env_filename][.stage], where stage comes from DOTSTAGE or the stage argument.
  5. Project Name Determination

    The project name is determined as follows:

    • If DOTPROJECT or project is defined, its basename is used
    • Otherwise, it defaults to the basename of the computed project_root directory

    Note:

    The project_root is computed relative to the file that directly calls load_env(), using the steps_to_project_root parameter.

    • If steps_to_project_root=0 (default), project_root is the directory containing the calling file
    • If steps_to_project_root=1, it's the parent of that directory, and so on

    For example:

    If load_env() is called from ~/projects/proj1/app/manage.py and steps_to_project_root=1, then project_root = ~/projects/proj1, and the fallback project name is proj1.

  6. .env Filename Construction

    The .env filename is constructed as:

    "[default_env_filename][[.]STAGE]"

    where:

    • default_env_filename is .env by default
    • STAGE comes from DOTSTAGEor stage9 (if defined)
  7. Primary Search Location

    If no explicit file path is provided via DOTENV/dotenv, the loader checks:

    [DOTCONFIG_ROOT | config_root] / [DOTPROJECT | project] / [default_env_filename][.stage]

  8. Fallback Location

    If the file is not found in the config directory, a fallback search is performed in the computed project root: [project_root] / [default_env_filename][.stage]

  9. Error Handling If no valid .env file is found after all resolution attempts, a FileNotFoundError is raised. The error message includes a list of all tried paths for easier debugging.

🎯 Use Cases

dotenv-loader is especially useful when:

  • Deploying applications to multiple environments (development, testing, staging, production).
  • Managing complex directory structures (monorepos or multi-app Django projects).
  • Simplifying CI/CD workflows by dynamically selecting environment configurations.

📜 License

MIT License

🤝 Contributing

We welcome contributions from the community! Please see CONTRIBUTING for details on how to get started.

🤖 Acknowledgments

This project was created in collaboration with ChatGPT (OpenAI), utilizing the GPT-4o, GPT-4.5, and GPT-3 models.

📅 Changelog

For detailed release notes, see CHANGELOG.


By clearly managing your environment variables and enabling dynamic configuration switching, dotenv-loader helps you streamline your deployment and development workflows, reduce errors, and maintain cleaner, more maintainable code.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dotenv_loader-1.0.3.tar.gz (15.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dotenv_loader-1.0.3-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

Details for the file dotenv_loader-1.0.3.tar.gz.

File metadata

  • Download URL: dotenv_loader-1.0.3.tar.gz
  • Upload date:
  • Size: 15.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for dotenv_loader-1.0.3.tar.gz
Algorithm Hash digest
SHA256 0e393cefe2bad07657c80d3d960f60ade616fbdb44648785d9c131b44d0f16e0
MD5 d77c5304f5a371063fb922a7845c8296
BLAKE2b-256 69a17ae3c1532789a752447975e895c4aa0d0d5e3683f334ac6b26351c5dcc12

See more details on using hashes here.

Provenance

The following attestation bundles were made for dotenv_loader-1.0.3.tar.gz:

Publisher: publish.yml on dbdeveloper/dotenv-loader

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dotenv_loader-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: dotenv_loader-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 9.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for dotenv_loader-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 416764f1e34321ea65d5e88c431988a29487eb08a901f33db2489231ebb2312d
MD5 86ab4516cef6e89dbf81e7a34ee42a51
BLAKE2b-256 66ef0f9e856ebab9245a448f40515b199bb46283bbaad240e6f9bc69a5e2316c

See more details on using hashes here.

Provenance

The following attestation bundles were made for dotenv_loader-1.0.3-py3-none-any.whl:

Publisher: publish.yml on dbdeveloper/dotenv-loader

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page