How to use environment variables in Python

Environment variables provide a secure way to store sensitive configuration data outside your Python code. These variables live in your system's environment, allowing you to manage API keys, database credentials, and other settings without hardcoding them.

This guide covers practical techniques for working with environment variables in Python, with real-world examples and debugging tips. All code examples were created with Claude, an AI assistant built by Anthropic.

Accessing environment variables with os.environ

import os

# Access an environment variable
username = os.environ.get('USER')
print(f"Current user: {username}")
Current user: johndoe

The os.environ module provides direct access to environment variables through a dictionary-like interface. While you could access variables directly with square bracket notation, the .get() method offers a safer approach by returning None instead of raising a KeyError when a variable doesn't exist.

Python's environment variables persist only during runtime and within the current process scope. This makes them ideal for:

  • Storing sensitive credentials without exposing them in version control
  • Configuring different settings across development, staging, and production environments
  • Managing application behavior through external configuration

Basic environment variable operations

Beyond the basic .get() method, Python's os.environ module offers several powerful ways to interact with and validate environment variables in your applications.

Using os.environ as a dictionary

import os

# Set a temporary environment variable
os.environ['APP_ENV'] = 'development'
print(f"Environment: {os.environ['APP_ENV']}")
Environment: development

The os.environ dictionary interface lets you directly modify environment variables during runtime. Setting a variable is as straightforward as using square bracket assignment: os.environ['KEY'] = 'value'.

  • Changes only affect the current Python process
  • Variables reset when the program terminates
  • Square bracket notation raises KeyError if the key doesn't exist

This approach works well for temporary configuration changes and testing scenarios. However, for persistent changes across sessions, you'll need to modify your system's environment variables through your operating system's interface.

Using os.getenv() with default values

import os

debug_mode = os.getenv('DEBUG_MODE', 'False')
port = os.getenv('PORT', '8000')
print(f"Debug mode: {debug_mode}, Port: {port}")
Debug mode: False, Port: 8000

os.getenv() provides a more flexible way to retrieve environment variables. Unlike os.environ.get(), it accepts a second parameter that serves as a fallback value when the requested variable doesn't exist.

  • The first argument specifies the environment variable name you want to access
  • The second argument sets a default value that returns if the variable isn't found
  • This approach eliminates the need for additional error handling or conditional checks in your code

In the example above, if DEBUG_MODE and PORT aren't set in the environment, Python will use 'False' and '8000' as their respective default values. This pattern works particularly well for configuration settings that have sensible defaults.

Checking if an environment variable exists

import os

if 'DATABASE_URL' in os.environ:
    print("Database URL is set")
else:
    print("Database URL is not set")
Database URL is not set

The in operator provides a straightforward way to verify if an environment variable exists before attempting to access it. This approach prevents potential errors that could crash your application.

  • Using in os.environ returns True if the variable exists and False otherwise
  • This method works well for conditional logic where you need different behavior based on variable presence
  • It's more explicit than catching exceptions from failed dictionary access

The example demonstrates a common pattern for checking database configurations. You can extend this pattern to provide fallback options or display helpful error messages when required variables are missing.

Advanced environment variable techniques

Building on these foundational techniques, Python offers powerful tools to manage environment variables through configuration files, type conversion, and temporary variable scoping.

Using python-dotenv to load from .env files

from dotenv import load_dotenv
import os

load_dotenv()  # Load variables from .env file
api_key = os.getenv('API_KEY')
print(f"API key loaded: {'Yes' if api_key else 'No'}")
API key loaded: No

The python-dotenv package simplifies environment variable management by loading variables from a .env file into your Python environment. This approach keeps sensitive data like API keys and passwords in a separate file that you can exclude from version control.

  • The load_dotenv() function reads key-value pairs from your .env file and adds them to os.environ
  • After loading, you can access these variables using familiar methods like os.getenv()
  • The package supports multiple environments by loading different .env files based on your configuration

This pattern works especially well for development teams sharing project configurations while keeping their individual credentials private and secure.

Converting environment variables to appropriate types

import os

max_connections = int(os.getenv('MAX_CONNECTIONS', '5'))
debug_enabled = os.getenv('DEBUG', 'False').lower() in ('true', '1', 't')
print(f"Max connections: {max_connections}, Debug enabled: {debug_enabled}")
Max connections: 5, Debug enabled: False

Environment variables always store values as strings. Converting them to appropriate Python data types ensures your application handles configuration values correctly. The example demonstrates two common type conversion patterns.

  • The int() function converts the MAX_CONNECTIONS string value to an integer, with a default of 5 if the variable isn't set
  • For boolean flags, the code converts DEBUG to lowercase and checks if it matches common truth values like 'true', '1', or 't'

This approach prevents type-related errors and provides consistent behavior across different environment configurations. Your application can now work with these values in their proper Python types instead of raw strings.

Using temporary environment variables with contextlib

import os
from contextlib import contextmanager

@contextmanager
def set_temp_env(key, value):
    old_value = os.environ.get(key)
    os.environ[key] = value
    try:
        yield
    finally:
        if old_value is None:
            del os.environ[key]
        else:
            os.environ[key] = old_value

with set_temp_env('TEMP_VAR', 'temporary'):
    print(f"Inside context: {os.environ['TEMP_VAR']}")
print(f"Outside context: {os.getenv('TEMP_VAR', 'not set')}")
Inside context: temporary
Outside context: not set

The contextlib module enables temporary environment variable management through Python's context manager pattern. The set_temp_env function creates a controlled scope where you can modify environment variables without affecting the broader system environment.

  • The function stores the original value of the environment variable before making changes
  • Inside the with block, your temporary value takes effect
  • Once the block ends, Python automatically restores the original value or removes the variable if it didn't exist before

This pattern proves especially useful for testing scenarios where you need to temporarily override configuration values. The try/finally block ensures proper cleanup even if errors occur during execution.

Configuring application behavior with os.environ

Environment variables enable dynamic application configuration by letting you adjust settings like database connections and debug modes based on your deployment environment without modifying code.

import os

# Configure application based on environment
env = os.environ.get('APP_ENVIRONMENT', 'development')
if env == 'production':
    debug = False
    database_url = os.environ['PROD_DB_URL']
else:
    debug = True
    database_url = os.environ.get('DEV_DB_URL', 'sqlite:///dev.db')
    
print(f"Running in {env} mode")
print(f"Debug: {debug}, Database: {database_url}")

This code demonstrates environment-based configuration management in Python applications. The os.environ.get() method checks for an APP_ENVIRONMENT variable and defaults to 'development' if not found. Based on this setting, the code configures two critical parameters:

  • Debug mode toggles between enabled for development and disabled for production
  • Database URL selection adapts to the environment. Production requires an explicit URL while development allows a SQLite fallback

The conditional logic creates a flexible system that automatically adjusts these settings. This pattern helps prevent accidental use of development configurations in production environments while maintaining convenient defaults for local development.

Building a secure API client with os.environ.get()

The create_api_client() function demonstrates how to build a secure API client that manages sensitive credentials and configuration settings through environment variables while providing sensible defaults for non-critical parameters.

import os
import requests

def create_api_client():
    api_key = os.environ.get('API_KEY')
    if not api_key:
        raise ValueError("API_KEY environment variable is required")
    
    base_url = os.environ.get('API_BASE_URL', 'https://api.example.com')
    timeout = int(os.environ.get('API_TIMEOUT', '30'))
    
    return f"Client configured with URL: {base_url}, timeout: {timeout}s"

try:
    client_info = create_api_client()
    print(client_info)
except ValueError as e:
    print(f"Error: {e}")

This code creates a flexible API client configuration system that prioritizes security. The create_api_client() function requires an API key through environment variables. It will raise a ValueError if the key isn't present.

  • The function retrieves essential settings like API_KEY, API_BASE_URL, and API_TIMEOUT from environment variables
  • It provides sensible defaults for the base URL and timeout settings
  • The try-except block gracefully handles missing API key errors

The code demonstrates robust error handling and configuration management. It ensures critical credentials are properly set while maintaining flexibility for optional parameters.

Common errors and challenges

Working with environment variables in Python introduces several common pitfalls that can impact your application's stability and security.

Handling KeyError when accessing non-existent variables

Directly accessing environment variables with square bracket notation (os.environ['KEY']) can crash your application if the variable doesn't exist. The code below demonstrates this common pitfall that occurs when required configuration values are missing from the environment.

import os

# This will raise a KeyError if DATABASE_URL is not set
database_url = os.environ['DATABASE_URL']
print(f"Connected to database at: {database_url}")

The code attempts to directly access a missing environment variable using os.environ['DATABASE_URL']. This triggers Python's built-in error handling mechanism, which immediately halts program execution. The next code example demonstrates a more resilient approach.

import os

# Using get() with a default value prevents KeyError
database_url = os.environ.get('DATABASE_URL', 'sqlite:///default.db')
print(f"Connected to database at: {database_url}")

Using os.environ.get() with a default value provides a safer way to access environment variables. This method returns the default value when the requested variable doesn't exist instead of crashing your application with a KeyError.

  • Always use .get() when the variable might not exist in the environment
  • Reserve direct dictionary access (os.environ['KEY']) for truly required variables where missing values should halt execution
  • Choose meaningful default values that won't cause issues downstream in your application

Watch for this pattern especially in deployment scripts and configuration management code where environment variables often change between different systems.

Type conversion issues with numeric environment variables

Environment variables store all values as strings. This creates a common pitfall when working with numeric data. A direct mathematical operation on an environment variable string value triggers a TypeError. The code below demonstrates this issue when attempting to multiply a connection limit value.

import os

# Setting a numeric environment variable
os.environ['MAX_CONNECTIONS'] = '10'

# This will cause an error if we try to use it directly in math operations
max_connections = os.environ['MAX_CONNECTIONS']
new_limit = max_connections * 2  # TypeError: can't multiply sequence by non-int
print(f"New connection limit: {new_limit}")

The error occurs because Python attempts to multiply a string value ('10') by an integer (2). The string comes directly from os.environ without proper type conversion. Let's examine the corrected approach in the next code block.

import os

# Setting a numeric environment variable
os.environ['MAX_CONNECTIONS'] = '10'

# Properly convert string to int before using in calculations
max_connections = int(os.environ['MAX_CONNECTIONS'])
new_limit = max_connections * 2
print(f"New connection limit: {new_limit}")

The solution explicitly converts the environment variable string to an integer using int() before performing mathematical operations. This prevents the TypeError that occurs when trying to multiply strings with numbers.

  • Watch for this issue when working with numeric configuration values like timeouts, limits, or port numbers
  • Always validate and convert environment variables to appropriate data types before using them in calculations
  • Consider wrapping conversions in error handling to catch invalid string formats

Dealing with case sensitivity in environment variable names

Environment variables in Python maintain their exact case sensitivity. A common error occurs when developers attempt to access variables with inconsistent capitalization. The os.environ dictionary strictly matches case, so DB_PASSWORD and db_password represent different variables.

import os

# Setting an environment variable
os.environ['DB_PASSWORD'] = 'securepassword'

# Attempting to retrieve with inconsistent casing (common mistake)
password = os.environ.get('db_password')
print(f"Database password: {password}")  # Will print None

The code fails because os.environ.get('db_password') searches for a lowercase variable name while the actual environment variable uses uppercase DB_PASSWORD. The case mismatch returns None instead of the stored value. Check out this corrected implementation:

import os

# Setting an environment variable
os.environ['DB_PASSWORD'] = 'securepassword'

# Use consistent casing when accessing environment variables
password = os.environ.get('DB_PASSWORD')
print(f"Database password: {password}")

The solution demonstrates proper case sensitivity handling when working with environment variables in Python. Using consistent casing between setting and retrieving variables prevents common access errors. The os.environ dictionary strictly matches case, so DB_PASSWORD and db_password are treated as entirely different variables.

  • Establish and follow a consistent naming convention for environment variables across your codebase
  • Document your chosen convention in project guidelines to prevent team confusion
  • Consider using uppercase for all environment variables. This aligns with common Unix conventions

Watch for this issue especially when working with configuration files or when multiple developers contribute to the same project. Case mismatches often surface during deployment when variables are set through different methods or platforms.

FAQs

How do I check if an environment variable exists before using it?

Environment variables store configuration data outside your code. To check if one exists, use os.getenv() in Python or process.env in Node.js. These methods return None or undefined for missing variables instead of raising errors.

This behavior lets you gracefully handle missing configurations. You can provide fallback values or display helpful error messages when required variables aren't set. Many frameworks automatically check for essential environment variables during startup to prevent runtime issues.

What happens when I try to access an environment variable that doesn't exist?

When you try to access a nonexistent environment variable, most programming languages return null or an empty string instead of throwing an error. This behavior enables defensive programming by letting you check if a variable exists before using it.

Operating systems handle this gracefully because environment variables frequently change between environments. A missing variable shouldn't crash your application—instead, your code should implement appropriate fallback logic.

Can I set environment variables from within my Python script?

Yes, you can modify environment variables in Python using os.environ. This dictionary-like object lets you get, set, and delete environment variables during runtime. However, these changes only affect the current process and its child processes. They won't persist after your script ends.

The operating system isolates environment variables by process for security. When you set variables with os.environ['KEY'] = 'value', Python updates the process environment table maintained by your OS. This isolation prevents one program from accidentally or maliciously affecting others.

How do I provide a default value when getting an environment variable?

The os.getenv() function accepts an optional second parameter that serves as the default value when an environment variable isn't found. For example, os.getenv('API_KEY', 'default_key') returns 'default_key' if API_KEY isn't set.

This pattern provides a safety net for your application, ensuring it can continue running even when expected environment variables are missing. You'll commonly use this approach when handling configuration values that might change between development and production environments.

What's the difference between using os.environ and os.getenv()?

The key difference lies in how these Python functions handle missing environment variables. os.environ behaves like a dictionary and raises a KeyError when a variable doesn't exist. os.getenv() returns None by default—making it safer for checking optional variables.

While os.environ allows direct modification of environment variables during runtime, os.getenv() provides read-only access with an option to specify default values. This makes os.getenv() ideal for configuration management where graceful fallbacks matter.

🏠