Environment Variables: Managing Application Configuration with python-dotenv

Managing configuration and credentials creates constant issues when building applications. Hardcoding sensitive values into code carries risk – what if they get checked into source control by accident? Popular solutions like environment variables or dedicated config services can add complexity.

The python-dotenv library solves this simply by externalizing configuration into easy .env files. This keeps secrets out of source code, simplifies setup across environments, and permits easy runtime overrides.

Why Use python-dotenv?

Here are key benefits of using python-dotenv:

Keep Credentials and Configs Out of Version Control

Checking API keys, database passwords, secrets, and other sensitive credentials into version control is risky. Python-dotenv allows defining these as variables in a .env file, which you can exclude from version control.

A common pattern is creating a .env.template file with placeholders for all variables needed, but no real values. Developers then create a local .env from this template and populate it.

Simplify Configuration Across Environments

Managing separate configs for local development, staging, production, etc. can prove difficult. With python-dotenv, you just point to a different .env file per environment:

# Local development
load_dotenv(.env)

# Staging 
load_dotenv(.env.staging)

No code changes needed!

Simple Setup

Getting started with python-dotenv requires just a few lines:

import os
from dotenv import load_dotenv

load_dotenv() # Load vars  

api_key = os.getenv("API_KEY") # Use vars!

The variables load from .env into os.getenv() and os.environ.

Creating and Loading .env Files

To use python-dotenv, first create a .env file. This typically goes in the project root.

Best practice is defining a variable structure upfront:

APP_ENV=local
DEBUG_MODE=true

DB_HOST=127.0.0.1
DB_NAME=devdb

Next call load_dotenv() early when the app starts:

“`python
import os
from dotenv import load_dotenv

load_dotenv() # Load .env vars

This parses `.env` and loads keys/values into the environment. You access them via `os.getenv()`:

python
db_host = os.getenv(“DB_HOST”) # Gets 127.0.0.1

### Ignoring .env from Version Control

To prevent checking secrets into version control, add `.env` to your `.gitignore`: 

.gitignore

.env

You may also create a checked-in `.env.template` file that defines variable structure sans values.

## Variable Priority and Overrides

Python-dotenv loads variables at lowest precedence. This permits other definitions like session environment variables or command line arguments to override `.env` values if needed.

The precedence order is:

1. **Command Line** - Highest priority  
2. **Session Environment** - Overrides `.env` variables
3. **.env File** - Lowest priority

For example, if `DEBUG=true` exists in `.env` but gets manually set to `false` at runtime, the manual variable takes effect.

This allows overriding configs per environment without changing code.

## Interpolation and Variable Substitution

Dotenv files support interpolating variables when building values:

API_URL=https://api.server.com

API_FULL_URL=${API_URL}/v1/query
“`

Here API_FULL_URL references API_URL to build a full value. This reduces duplication.

Managing Production

In production deployments, .env files may not always store credentials. Solutions like Kubernetes secrets or cloud-based variable injection are common.

The python-dotenv benefit is no code change is needed. The load_dotenv() call does nothing in production, while session/environment variables take precedence. This enables a single codebase working across all environments.

Conclusion

By externalizing environment-specific configuration into easy .env files, python-dotenv streamlines configuration across projects and environments.

To start:

  1. Install python-dotenv
  2. Create .env with variables
  3. Call load_dotenv() to load into app!

Python-dotenv removes hardcoding credentials while managing configs safely across environments. Give it a try in your next Python application!

Frequently Asked Questions

How do I ignore .env from version control?

Simply add .env to your .gitignore file. This will exclude it from Git tracking.

What happens if a variable isn’t defined in .env?

Using os.getenv() will return None. With dictionary-style access like os.environ[], a KeyError gets raised.

Can I use interpolation for non-string variables?

Yes, interpolation works for any value type defined in .env.

Is python-dotenv compatible with frameworks like Flask and Django?

Absolutely! The library is framework-agnostic. Just ensure load_dotenv() gets called early at startup.

How do I handle production secrets?

Solutions like cloud secret managers and Kubernetes secrets are preferred. Python-dotenv allows falling back to environment variables.