What a .env file is and why Python can't read it automatically
A .env file is a plain text file that stores configuration values your Python program needs — database passwords, API keys, server addresses, feature flags. Python doesn't have built-in code to read .env files the way it reads .py files. You have to write that code yourself or use a library that does it for you.
The .env file sits in your project folder but stays out of version control (you add it to .gitignore so secrets don't end up on GitHub). When your program runs, it needs to open that file, parse each line, and load the values into your environment so your code can access them.
The most common approach is the python-dotenv library, which handles the file reading and parsing in a few lines. If you prefer not to add a dependency, you can write a function to read the file yourself — it's straightforward but requires more code to handle edge cases.
Key Takeaways
- The python-dotenv library is the standard way to load .env files in Python; install it with pip install python-dotenv and call load_dotenv() at the start of your program.
- After calling load_dotenv(), access variables with os.getenv('VARIABLE_NAME') or os.environ['VARIABLE_NAME'].
- Your .env file should list variables as KEY=value pairs, one per line, with no spaces around the equals sign unless they are part of the value itself.
- Add .env to your .gitignore file so the file never gets committed to version control and your secrets stay local to your machine.
- If you write your own .env reader instead of using python-dotenv, you still need to handle quoted values, comments, and empty lines correctly.
Using python-dotenv to load variables
Install python-dotenv from the command line:
pip install python-dotenv
At the top of your main Python file (or any file that runs first), import and call the load function:
from dotenv import load_dotenvimport osload_dotenv()
This reads your .env file and adds each variable to os.environ. After that line runs, you can access any variable from the .env file using os.getenv():
database_url = os.getenv('DATABASE_URL')api_key = os.getenv('API_KEY')
If a variable is not in the .env file, os.getenv() returns None by default. You can provide a fallback value as the second argument: os.getenv('DEBUG', 'False') returns 'False' if DEBUG is not set.
Format your .env file correctly
Create a file named .env in your project root (the same folder as your main Python script or your requirements.txt). Each line is a key-value pair:
DATABASE_URL=postgresql://user:password@localhost/mydbAPI_KEY=abc123def456DEBUG=TrueSECRET_KEY=my-secret-key-here
Do not use spaces around the equals sign unless the space is part of the actual value. Comments start with a hash and take up the whole line:
# Database configurationDATABASE_URL=postgresql://user:password@localhost/mydb# API settingsAPI_KEY=abc123def456
If a value contains spaces or special characters, wrap it in double quotes:
DESCRIPTION="This is a longer value with spaces"WEBHOOK_URL="https://example.com/webhook?token=abc&id=123"
Empty lines are ignored. python-dotenv handles quoted values automatically, so you do not need to strip quotes in your Python code — os.getenv('DESCRIPTION') returns the string without the quotes.
Specify a different .env file location
By default, load_dotenv() looks for a file named .env in the current working directory. If your .env file is in a different folder or has a different name, pass the path to load_dotenv():
from dotenv import load_dotenvimport osload_dotenv('/path/to/your/.env')
Use an absolute path if your script might run from different directories. If you are in a web framework like Flask or Django, the working directory is usually the project root, so load_dotenv() with no argument usually works.
You can also pass a relative path from your script's location. If your script is in a subfolder and the .env is in the parent folder, use:
load_dotenv('../.env')
Read .env without the python-dotenv library
If you want to avoid adding a dependency, you can write a function to parse the .env file yourself. This function reads the file line by line and loads each variable into os.environ:
import osdef load_env_file(filepath='.env'): if not os.path.exists(filepath): return with open(filepath) as f: for line in f: line = line.strip() if not line or line.startswith('#'): continue if '=' in line: key, value = line.split('=', 1) os.environ[key.strip()] = value.strip()load_env_file()
This version skips empty lines and comments, splits on the first equals sign (so values can contain equals signs), and strips whitespace from keys and values. It does not handle quoted values the way python-dotenv does, so if you need that feature, add logic to detect and remove quotes.
For most projects, python-dotenv is simpler and more robust. Write your own only if you have a specific reason to avoid the dependency.
Troubleshooting common problems
Variables are None or not found: Make sure load_dotenv() runs before you try to access the variables. If your .env file is in a different folder, pass the correct path. Check that the file is named exactly .env (not .env.txt or .env.local unless you specify that path).
Changes to .env don't show up: Python loads the .env file once when load_dotenv() runs. If you edit the .env file while your program is running, restart the program to pick up the changes. During development, you may need to stop and restart your server or script.
Values have extra quotes or spaces: If you see quotes in your variable value, you wrapped them in quotes in the .env file but python-dotenv did not strip them. Use double quotes in .env and python-dotenv will remove them. If you wrote your own parser, add quote-stripping logic.
Import error for dotenv: If you see ModuleNotFoundError: No module named 'dotenv', you have not installed python-dotenv yet. Run pip install python-dotenv in the same environment where your Python script runs.
Frequently Asked Questions
Can I use os.environ directly without a .env file?
Yes. You can set environment variables in your shell or system settings, and Python reads them with os.environ or os.getenv(). The .env file is a convenience for local development so you do not have to set variables manually each time you open a terminal. In production, most teams set environment variables through their hosting platform (Heroku, AWS, Docker, etc.) rather than using a .env file.
Should I commit the .env file to Git?
No. Add .env to your .gitignore so it never gets committed. Your .env file contains secrets like API keys and database passwords. Create a .env.example file instead, with the same keys but dummy or empty values, so other developers know what variables they need to set locally.
What if I have multiple .env files for different environments?
You can load different files based on an environment variable. For example: env = os.getenv('ENV', 'development')load_dotenv(f'.env.{env}') loads .env.development, .env.production, etc. Some teams use a single .env file and override specific values in production through their hosting platform's settings.
Can I access .env variables in a Django or Flask app?
Yes. Call load_dotenv() at the very top of your settings.py (Django) or app initialization file (Flask), before any other imports that might need those variables. In Django, this is usually the first line of settings.py. In Flask, put it in your create_app() function or __init__.py.
What happens if a variable is in .env but I never use it?
Nothing. load_dotenv() adds all variables to os.environ, but your code only reads the ones you call with os.getenv(). Unused variables take up no memory and do not affect your program.