Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Work with Environment Variables

Learn how to read, set, and manage environment variables using Python's os.environ module in this practical tutorial.

Focus: work with environment variables and os.environ

Sponsored

Have you ever hardcoded API keys, database URLs, or secret tokens directly into your Python scripts? It's a security nightmare and a maintenance headache — every time those values change, you have to dig into your code and hope you didn't miss a spot. Environment variables solve this problem by keeping sensitive configuration outside your source code, letting you change settings without touching a single line of Python. In this lesson, you'll learn how to work with environment variables using Python's built-in os.environ dictionary — the standard way to read, set, and even delete these powerful settings across your operating system.

The problem this lesson solves

Modern applications depend on countless configuration values: API endpoints, secret keys, database credentials, feature flags, and more. When you embed them in your code, you create several dangerous problems:

  • Security risks — Anyone with access to your repository can see your secrets.
  • Portability issues — Your code might work on your machine but fail on a server with different paths or ports.
  • Hard-coded failures — A simple typo or environment change requires a code redeploy.
  • Testing nightmares — Switching between development, staging, and production means rewriting constants.

Environment variables are the industry-standard way to decouple configuration from code. They're supported by every operating system, every CI/CD pipeline, and every cloud platform. By mastering os.environ, you'll write cleaner, safer, and more portable Python applications.

Core concept / mental model

Think of os.environ as a live dictionary that your operating system maintains. When your Python program starts, it inherits a snapshot of all currently set environment variables from the parent process (your shell, IDE, or system service). You can read from this dictionary at any time, and if you make changes (in most cases), those changes only affect your running process — not the system globally.

What is os.environ?

os.environ is a mapping object (similar to a Python dictionary) that represents the environment variables of the current process. Each key is a variable name (always a string), and each value is its string value. It's available from the standard library module os, so no extra installs are needed.

Pro tip: Unlike a regular dictionary, os.environ behaves like a dictionary but with OS-level side effects. When you modify it, the change is immediately reflected in your current process and any child processes you spawn.

Key characteristics

  • Keys and values are always strings — If you need numbers or booleans, you must convert them manually.
  • Case-sensitive on most systemsMY_VAR and my_var are different variables on Linux/macOS (though Windows is case-insensitive).
  • Immutable at the OS level for the current process — You can't truly delete a variable from the OS; you can only unset it for your running program.
  • Thread-safe to read but not thread-safe to modify — If you have multiple threads writing to os.environ, wrap modifications in a lock.

How it works step by step

Let's walk through the lifecycle of using environment variables in Python:

Step 1: Import the os module

Every environment variable interaction requires importing os:

import os

Step 2: Read an environment variable

The most common operation: access the value using its key. If the variable doesn't exist, Python raises a KeyError — just like a missing dictionary key.

# Safe way: use .get()
db_url = os.environ.get('DATABASE_URL')
if db_url is None:
    print('Warning: DATABASE_URL not set. Using default.')
    db_url = 'postgres://localhost:5432/mydb'

# Or with a default value inline
secret_key = os.environ.get('SECRET_KEY', 'default-dev-key')

Step 3: Set an environment variable for the current process

This is useful for configuring your program dynamically:

os.environ['MY_APP_MODE'] = 'development'
# Now your code can check os.environ['MY_APP_MODE']

Step 4: Delete (unset) an environment variable

Use del or pop() to remove a variable from your process's environment:

if 'TEMP_SECRET' in os.environ:
    del os.environ['TEMP_SECRET']

# Or use pop (returns the value or a default)
ol_value = os.environ.pop('TEMP_SECRET', None)

Step 5: List all environment variables

Sometimes you need to inspect the whole environment (for debugging or logging):

for key, value in sorted(os.environ.items()):
    print(f'{key}={value}')

Hands-on walkthrough

Now you'll write a complete script that reads, sets, and validates environment variables. Create a file named env_demo.py:

import os

def get_config():
    """Read required configuration from environment variables."""
    config = {
        'API_KEY': os.environ.get('MY_API_KEY'),
        'DATABASE_URL': os.environ.get('DATABASE_URL'),
        'DEBUG_MODE': os.environ.get('DEBUG', 'false').lower() == 'true',
        'PORT': int(os.environ.get('PORT', 8000)),
    }

    # Validate required variables
    missing = [k for k, v in config.items() if v is None and k != 'DEBUG_MODE']
    if missing:
        raise EnvironmentError(f"Missing required env vars: {', '.join(missing)}")

    return config

# Example usage
if __name__ == '__main__':
    # Set a local env var for this run
    os.environ['MY_API_KEY'] = 'sk-test-12345'
    os.environ['DATABASE_URL'] = 'postgres://user:pass@localhost/mydb'
    os.environ['DEBUG'] = 'true'

    try:
        cfg = get_config()
        print('Configuration loaded successfully:')
        for key, value in cfg.items():
            print(f'  {key}: {value}')
    except EnvironmentError as e:
        print(f'Error: {e}')

Expected output:

Configuration loaded successfully:
  API_KEY: sk-test-12345
  DATABASE_URL: postgres://user:pass@localhost/mydb
  DEBUG_MODE: True
  PORT: 8000

Now run the same script without setting the environment variables (comment out the four os.environ lines) — you'll see the error raised because MY_API_KEY and DATABASE_URL are missing.

Practice: check if a variable exists

import os

if 'PATH' in os.environ:
    print('PATH is set.')
    paths = os.environ['PATH'].split(os.pathsep)
    print(f'You have {len(paths)} directories in your PATH.')
else:
    print('PATH is not set — unusual.')

Expected output (varies):

PATH is set.
You have 8 directories in your PATH.

Compare options / when to choose what

There are a few ways to manage environment variables in Python. Here's a comparison to help you choose:

Approach Use case Pros Cons
os.environ[key] Simple read/write Built-in, no dependencies, immediate effect Only strings, KeyError if missing
os.environ.get(key, default) Safe read No exception, easy defaults Still strings, no type conversion
os.getenv(key, default) Read-only Slightly shorter, same as .get() Cannot set values
.env file + python-dotenv Development settings Easy to share with teammates, keeps secrets out of repo Extra library, not native
Third-party config libraries (e.g., pydantic-settings) Complex configs Type coercion, validation, schema Heavier dependency, learning curve

When to choose what: - Use os.environ or os.getenv for quick scripts or when you need to modify the environment dynamically. - Use a .env file with python-dotenv for projects with multiple environment variables that differ per developer (make sure to add .env to .gitignore). - Use pydantic-settings for large applications requiring strict validation and nested configuration (e.g., FastAPI projects).

Troubleshooting & edge cases

1. KeyError when reading a missing variable

# This crashes:
db_url = os.environ['DATABASE_URL']  # KeyError if not set

Fix: Always use .get() with a default, or check 'DATABASE_URL' in os.environ first.

2. Setting a variable doesn't persist after script ends

# Inside a script:
os.environ['TEMP_VAR'] = 'hello'
# After script exits, os.environ reverts to the shell's original state

This is by design — environment variable changes are not permanent. To persist, you must modify shell configuration files (.bashrc, .zshrc) or system-level settings.

3. Type coercion fails silently

os.environ['PORT'] = 'abc'
port = int(os.environ['PORT'])  # ValueError: invalid literal for int()

Fix: Always validate after conversion, or use a default with a try/except.

4. Large environment slows down startup

On some operating systems, if you have thousands of environment variables (rare but possible), iterating over os.environ can be slow. Only iterate when necessary.

5. Environment variables inherited from parent

If you run your script from a shell with certain variables set, your Python script inherits them automatically. Be aware that running your script via sudo or in a container may have a different environment.

What you learned & what's next

In this lesson, you learned: - How to read, set, and delete environment variables using os.environ. - That os.environ is a dictionary-like mapping that always stores strings. - The difference between os.environ[key], .get(), and os.getenv(). - How to validate required environment variables and handle missing ones gracefully. - When to use plain os.environ versus third-party libraries like python-dotenv.

You're now ready to write configuration-aware Python scripts that can run anywhere — from your laptop to a production server. The next lesson in this track builds on this foundation: Working with configuration files (e.g., YAML or JSON). You'll learn how to read and merge settings from environment variables, config files, and command-line arguments for even more robust applications.

Practice recap

Write a small script that reads a LOG_LEVEL environment variable (default to 'INFO'), then configures Python's logging module accordingly. Then set the variable in your shell before running the script and verify the log level changes. This exercise will solidify your understanding of reading environment variables and using them to influence program behavior.

Common mistakes

  • Using os.environ[key] without checking existence — always use .get() or in to avoid KeyError.
  • Assuming environment variable values are typed (e.g., treating 'True' as boolean) — they are always strings; convert explicitly.
  • Modifying os.environ in a multithreaded script without a lock — concurrent modifications can cause data races.
  • Forgetting that changes to os.environ are only visible to the current process and any child processes, not the parent shell or other running programs.

Variations

  1. Use os.getenv(key, default) as a read-only alternative to os.environ.get() — slightly shorter but cannot set values.
  2. Load variables from a .env file with python-dotenv for development — keeps secrets out of version control but requires an extra library.
  3. For type-safe configuration, use pydantic-settings (or older envparse) to automatically parse and validate environment variables into typed Python objects.

Real-world use cases

  • Storing database credentials (host, port, user, password) in environment variables so the same code works in dev, staging, and production without changes.
  • Setting feature flags (e.g., ENABLE_NEW_FEATURE=true) to toggle behavior in deployed applications without redeploying code.
  • Configuring cloud service endpoints and API keys in containerized environments (Docker/Kubernetes) where secrets are injected at runtime.

Key takeaways

  • Environment variables keep configuration outside code, improving security and portability.
  • os.environ is a dictionary-like object where all keys and values are strings — always convert types manually.
  • Always use .get() with a default to read variables safely and avoid KeyError.
  • Changes to os.environ affect only the current process and its children, not the system globally.
  • For projects with many variables, consider a .env file with python-dotenv but never commit secrets to version control.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.