Manage application settings with ease using runenv, a lightweight tool inspired by The Twelve-Factor App methodology for configuration through environment variables.
runenv provides:
- A CLI for language-agnostic
.envprofile execution - A Python API for programmatic
.envloading
βStore config in the environmentβ β 12factor.net/config
| Section | Status |
|---|---|
| CI/CD | |
| PyPI | |
| Python | |
| Style | |
| License | |
| Docs | CHANGELOG.md |
- Key Features
- Quick Start
- Multiple Profiles
- Framework Integrations
- Parsing Behaviour
- Sample
.envFile - Similar Tools
- π CLI-First: Use
.envfiles across any language or platform. - π Python-native API: Load and transform environment settings inside Python.
- βοΈ Multiple Profiles: Switch easily between
.env.dev,.env.prod, etc. - βοΈ Multiple Formats: Use plain
.env,.env.json,.env.toml, or.env.yaml - βοΈ Autodetect Env File: Looking for
.env,.env.json,.env.toml, and.env.yaml - π§ Parameter Expansion: Bash-style
${VAR:-default},${VAR:?msg},${VAR:+alt}operators. - π§© Framework-Friendly: Works well with Django, Flask, FastAPI, and more.
pip install runenv
pip install runenv[toml] # if you want to use .env.toml in python < 3.11
pip install runenv[yaml] # if you want to use .env.yamlRun any command with a specified environment:
runenv run --env-file .env.dev -- python manage.py runserver
runenv run --env-file .env.prod -- uvicorn app:app --host 0.0.0.0
runenv list [--env-file .env] # view parsed variables
runenv lint [--env-file .env] # check common errors in env fileNote: The
load_envwill not parse env_file if therunenvCLI was used, unless youforce=Trueit.
from runenv import load_env
load_env() # loads .env
load_env(
env_file=".env.dev", # file to load - will be autodetected if not passed
prefix='APP_', # load only APP_.* variables from file
strip_prefix=True, # strip ^ prefix when loading variables
force=True, # load env_file even if the `runvenv` CLI was used
search_parent=1, # look for env_file in current dir and its 1 parent dirs
require_env_file=False # raise error if env file is missing, otherwise just ignore
)from runenv import create_env
config = create_env() # parse .env content into dictionary
config = create_env(
env_file=".env.dev", # file to load - will be autodetected if not passed
prefix='APP_', # parse only APP_.* variables from file
strip_prefix=True, # strip ^ prefix when parsing variables
search_parent=1, # look for env_file in current dir and its 1 parent dirs
)
print(config)Options include:
- Filtering by prefix
- Automatic prefix stripping
- Searching parent directories
Use separate .env files per environment:
runenv .env.dev flask run
runenv .env.staging python main.py
runenv .env.production uvicorn app.main:appRecommended structure:
.env.dev
.env.test
.env.staging
.env.production
Note: If you're using
runenv .env [./manage.py, ...]CLI then you do not need change your code. Use these integrations only if you're using Python API.
# manage.py or wsgi.py
from runenv import load_env
load_env(".env")from flask import Flask
from runenv import load_env
load_env(".env")
app = Flask(__name__)from fastapi import FastAPI
from runenv import load_env
load_env(".env")
app = FastAPI()| Situation | Behaviour |
|---|---|
| Duplicate key | Last definition wins; a warning is emitted by lint |
Key exactly equal to --prefix |
Skipped (stripping would produce an empty name) |
| Key without matching prefix | Skipped and reported as info by lint |
Duplicate keys are not an error β the last value in the file takes effect, matching the behaviour of most shell .env loaders. Use runenv lint to surface duplicates as warnings before they reach production.
${VAR} references resolve against variables defined in the same file and then fall back to the calling shell's os.environ. The following bash-style parameter expansion operators are supported:
| Syntax | Behaviour |
|---|---|
${VAR} |
Value of VAR; empty string if unset |
${VAR:-default} |
Value of VAR if set and non-empty, otherwise default |
${VAR-default} |
Value of VAR if set (even if empty), otherwise default |
${VAR:?msg} |
Value of VAR if set and non-empty; fatal error with msg otherwise |
${VAR:+alt} |
alt if VAR is set and non-empty, otherwise empty string |
The :? operator causes runenv run / runenv list to exit non-zero and runenv lint to report an error-level message with the line number where the variable is declared.
# Basic
DEBUG=1
PORT=8000
# export keyword is accepted (and discarded) for shell-source compatibility
export HOST=localhost
URL=http://${HOST}:${PORT}
# Default values via parameter expansion
CACHE_URL=${REDIS_URL:-redis://localhost:6379}
LOG_LEVEL=${LOG_LEVEL-info}
# Required variable β fails with a clear message if unset
SECRET=${APP_SECRET:?APP_SECRET must be set before running}
# Conditional value β only set when FEATURE_FLAG is enabled
FEATURE_HEADER=${FEATURE_FLAG:+X-Feature: on}
# Quotes and comments
EMAIL="admin@example.com" # Inline comment
TOKEN='s3cr3t'- python-dotenv β Python-focused, lacks CLI tool
- envdir β Directory-based env manager
- dotenv-linter β Linter for
.envfiles
With runenv, you get portable, scalable, and explicit configuration management that aligns with modern deployment standards. Ideal for CLI usage, Python projects, and multi-environment pipelines.