Using python-dotenv to Load Environment Variables from .env
python dotenv load environment variables from .env: Learn how to use python-dotenv to load environment variables from .env files, including installation, override beha...
python dotenv load environment variables from .env requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When you need to load environment variables from a .env file in a Python project, the python-dotenv library is the standard solution. It reads key-value pairs from a .env file and makes them available through os.environ. This article explains how to use python dotenv to load environment variables from .env files, covering installation, common usage patterns, configuration options, and security considerations.
Installing python-dotenv
Install the package with pip:
pip install python-dotenv
If you use Poetry or pipenv, add it as a regular dependency. For development-only use, you can mark it as a dev dependency, but many projects include it in production because the .env file may be used in deployment environments.
Loading the .env File with load_dotenv
The most common function is load_dotenv(). By default, it looks for a file named .env in the current working directory and loads its contents into the environment.
from dotenv import load_dotenv import os load_dotenv() db_host = os.getenv("DB_HOST") print(db_host)
load_dotenv() does not overwrite existing environment variables by default. If a variable is already set in the process environment, the value from .env is ignored. To override existing values, pass override=True:
load_dotenv(override=True)
This is useful in local development when you want the .env file to take precedence over variables set in your shell.
Understanding the Override Behavior
The default behavior prevents .env from masking real environment variables, which is important in production where you might have variables set by the deployment system. For example, if DB_HOST is already set in the environment, load_dotenv() will not change it. This avoids accidental overrides that could break the application.
If you need to force the .env values to take effect, use override=True. However, be cautious: this can hide configuration errors if the .env file contains stale or incorrect values.
Using dotenv_values for Dictionary Access
Sometimes you don't want to modify the process environment. The dotenv_values() function returns a dictionary of the parsed .env file without touching os.environ.
from dotenv import dotenv_values config = dotenv_values(".env") print(config.get("API_KEY"))
This is useful for configuration loading where you want to merge values from different sources or validate them before applying. It also avoids side effects on the global environment.
Handling Missing or Invalid .env Files
load_dotenv() returns False if the file is not found, but it does not raise an exception. This can be problematic if the .env file is required for the application to run. You can explicitly check the return value:
from dotenv import load_dotenv if not load_dotenv(): raise RuntimeError("Missing .env file")
For invalid lines, python-dotenv silently ignores lines that don't follow the KEY=VALUE format. To catch syntax errors, you can use dotenv_values() and validate the result yourself. The library does not provide a strict parser, so it's your responsibility to ensure the file is correctly formatted.
Security Considerations for .env Files
.env files often contain secrets like API keys, database credentials, and tokens. It's critical to keep them out of version control. Add .env to your .gitignore file:
.env
For production, consider using a secret management service instead of a .env file. If you do use .env in production, ensure the file permissions are restricted (e.g., chmod 600). Also, avoid committing a .env.example file that contains real values; use placeholders.
Integration with Configuration Libraries
python-dotenv can be combined with configuration libraries like pydantic or dynaconf. For example, with pydantic:
from pydantic import BaseSettings from dotenv import load_dotenv load_dotenv() class Settings(BaseSettings): database_url: str api_key: str settings = Settings()
This pattern loads the .env file first, then lets the configuration library read from the environment. It keeps your configuration logic separate from environment loading.
Common Mistakes and How to Avoid Them
One common mistake is calling load_dotenv() after importing modules that read environment variables at import time. Environment variables are read at runtime, so the call must happen before any code that depends on them. For example:
import os from dotenv import load_dotenv load_dotenv() # Now safe to read env vars value = os.getenv("VALUE")
Another mistake is using relative paths for the .env file. load_dotenv() looks in the current working directory, which may not be the project root when running scripts from elsewhere. Use find_dotenv() to locate the file relative to the script:
from dotenv import load_dotenv, find_dotenv load_dotenv(find_dotenv())
find_dotenv() searches upward from the calling file to find a .env file, making your script location-independent.