How to Manage Environment Variables in a Python Project
API keys and database credentials change between machines and environments; hardcoding them leaks secrets into version control. A .env file keeps these values outside the code, where each environment supplies its own.
Prerequisites
- uv installed
- A uv project (run
uv initto create one)
Create a .env file
Add a .env file to the project root with one KEY=value pair per line:
DATABASE_URL=postgres://localhost:5432/mydb
SECRET_KEY=change-me-in-production
API_KEY=sk-test-abc123Add .env to .gitignore
Warning
Add this line before the first commit that contains the .env file. Removing a file after it has been pushed does not erase it from Git history.
Open .gitignore and add:
.envProjects created with uv init ship a .gitignore, but it does not include .env by default. For full .gitignore setup, see how to put your Python project on GitHub.
Load variables with uv run --env-file
uv’s --env-file flag injects variables into the process environment before Python starts. uv run executes the command inside the project’s virtual environmentAn isolated folder where Python installs packages for one project, keeping them separate from other projects and your system Python.
Learn more →
, ensuring all dependencies are installed first. Without --env-file, the script raises KeyError: 'DATABASE_URL' because no environment variable is set.
uv run --env-file .env python main.pyimport os
database_url = os.environ["DATABASE_URL"] # raises KeyError if missing
api_key = os.getenv("API_KEY", "fallback") # returns "fallback" if missingPass --env-file multiple times to layer files (later files override earlier ones). Set UV_ENV_FILE=.env in the shell to avoid typing the flag on every invocation. If the shell and the .env file both define the same variable, the shell value wins.
Load variables with python-dotenv
When the project needs variables available at import time without passing a CLI flag, install python-dotenv:
uv add python-dotenvCall load_dotenv() at the top of the entry point:
from dotenv import load_dotenv
import os
load_dotenv() # reads .env; does not override variables already set in the shell
database_url = os.environ["DATABASE_URL"]Document required variables with .env.example
Create a .env.example that lists every variable the project expects, with placeholder values:
DATABASE_URL=postgres://user:password@localhost:5432/dbname
SECRET_KEY=change-me
API_KEY=your-api-key-hereCommit .env.example to version control. New contributors copy it to .env and fill in real values. Keep it updated when adding or removing variables.
Keep secrets away from AI coding agents
AI coding assistants read project files by default, including .env. Add .env to the agent’s ignore file (.claudeignore for Claude Code, .cursorignore for Cursor) to prevent it from reading secrets:
.envLearn More
- uv run CLI reference documents
--env-file,--no-env-file, andUV_ENV_FILE. - python-dotenv documentation covers interpolation, encoding, and stream parsing.
- How to put your Python project on GitHub covers
.gitignoresetup for new projects. - direnv loads
.envrcfiles automatically when entering a directory instead of requiring manual--env-fileflags.