Python

Managing Environment Variables in Python

Learn how to manage environment variables in Python using `os.environ`, `python-dotenv`, and validation libraries like `pydantic-settings`. Keep secrets out of your code and handle config safely across development and production.

August 2026 6 min read 14 views 0 hearts

Managing Environment Variables in Python: What You Need to Know

If you've ever written a Python app that needs to connect to a database or an API, you've probably run into the question of where to store your secret keys, passwords, or configuration settings. Hardcoding these values in your code might work for a small test, but it quickly becomes a security nightmare. That's where environment variables step in.

Python handles environment variables with a few straightforward tools that are easy to understand and use. Let's break down how it works, and what you need to keep in mind.

The Basics: os.environ

Python's os module gives you access to environment variables through a dictionary-like object called os.environ. Here's the simplest way to get a value:

import os

database_url = os.environ.get('DATABASE_URL')

If the variable doesn't exist, .get() returns None by default, which is safer than throwing an error. You can also provide a fallback:

port = os.environ.get('PORT', '5000')

This approach is great for scripts and small projects. But for serious work, you'll want a more structured solution.

Why Not Just Use os.environ Everywhere?

The os.environ dictionary works, but it has a few quirks. All the values are strings, so you have to convert them manually. Boolean values like 'True' or 'false' are just strings, not actual booleans. Environment variables can also be missing or misspelled, and your code might not catch the error until runtime.

This is why most professional Python developers reach for more robust tools.

The Modern Way: python-dotenv

The python-dotenv library is a game changer for managing configuration in development. It lets you keep your environment variables in a .env file that sits in your project root, but never gets committed to version control.

First, install it:

pip install python-dotenv

Then create a .env file:

DATABASE_URL=postgresql://user:password@localhost:5432/mydb
API_KEY=sk-1234abcd5678
DEBUG=true

In your code, you load these variables with:

from dotenv import load_dotenv
import os

load_dotenv()

database_url = os.getenv('DATABASE_URL')
api_key = os.getenv('API_KEY')

The beauty of this is that load_dotenv() only runs in development. In production, you set the environment variables directly on your server, and the os.getenv() calls still work without any code changes. No magic, just practical separation of config from code.

A Real‑World Example at PythonSkillset

Here at PythonSkillset, we run a small dashboard that tracks API usage for our readers. When we deployed it, we needed to keep our API keys and database credentials separate from the code. The .env file made local development simple, and when we moved to a cloud server, we just set those same variables in the server's environment settings. The Python code never changed, and no secrets leaked into our repository.

# config.py
import os
from dotenv import load_dotenv

load_dotenv()

class Settings:
    DATABASE_URL = os.getenv('DATABASE_URL')
    API_KEY = os.getenv('API_KEY')
    DEBUG = os.getenv('DEBUG', 'false').lower() == 'true'

That single config.py module became the central place where all environment variables were loaded. Any part of the app could import Settings.DATABASE_URL and know the value was correct.

Validating Your Environment Variables

One problem remains: if a required environment variable is missing, your app might fail halfway through. To catch this early, use a validation library like pydantic-settings or environs.

With pydantic-settings, you define a settings class that automatically parses and validates environment variables:

pip install pydantic-settings
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    api_key: str
    debug: bool = False

    class Config:
        env_file = '.env'

settings = Settings()

If database_url is missing, this raises a clear error the moment your app starts. No more wondering why your database connection silently fails half an hour later.

Common Pitfalls to Avoid

  • Don't hardcode default values for secrets. A default like 'password123' in production is dangerous. Only provide defaults for non‑sensitive settings like port numbers or log levels.
  • Don't commit your .env file. Add it to .gitignore immediately. Use a .env.example file instead, with dummy values.
  • Watch out for trailing spaces. An environment variable like SECRET_KEY= mykey (with a space after the =) will include that space. It's a subtle bug that can drive you crazy.
  • Remember that environment variables are case‑sensitive on Linux. Database_Url and database_url are different.

When to Use What

Situation Tool
Quick script or one‑off job os.environ
Flask/Django/FastAPI project python-dotenv + os.getenv
Large application with many settings pydantic-settings or environs
Docker containers Set env vars in docker-compose.yml or Dockerfile
Production server System env vars (set by your deployment tool)

Final Thoughts

Managing environment variables in Python is not complicated once you understand the basics. Start with os.environ for small tasks, add python-dotenv for clean development setups, and move to validation libraries when your application grows. The key takeaway is simple: keep your configuration outside your code. Your future self—and anyone else working on your project—will thank you.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.