KAHIBARO
Discord Login Register

5.14. Environment Variables

Why Environment Variables Matter

Environment variables are simple text values that your operating system provides to programs. Backend applications use them to store configuration that can change between machines and environments, for example:

The key idea: code stays the same, configuration changes via environment variables.

This gives you:

Always keep secrets and credentials in environment variables or other secure configuration, never directly in code or in your Git repository.


Common Environment Variables

You can define anything you like, but some names are common in backend projects:

VariableExample valuePurpose
DATABASE_URLpostgresql://user:pass@localhost:5432/appDB connection string
SECRET_KEYs3cr3t-r4nd0m-str1ngFor signing tokens, sessions, etc.
DEBUGtrue or falseToggle debug features
ENV / APP_ENVdevelopment, staging, productionCurrent environment name
PORT8000Port for web server
REDIS_URLredis://localhost:6379/0Redis connection
SMTP_HOSTsmtp.gmail.comEmail provider host

You can pick your own names. Consistency across your project is what matters.


Viewing and Setting Environment Variables

How you set variables depends on the operating system and the shell.

On Linux and macOS (Bash / Zsh)

View all environment variables

bash
printenv           # or
env

View a single value

bash
echo "$HOME"
echo "$PATH"
echo "$DATABASE_URL"

Set a variable for the current shell session

bash
export DEBUG=true
export DATABASE_URL="postgresql://user:pass@localhost:5432/app"

After this, any program started from this shell (like python app.py) will see those variables.

Set a variable for a single command only

bash
DEBUG=true python app.py
PORT=9000 uvicorn main:app

After the command finishes, the variable is not kept.

Unset a variable

bash
unset DEBUG

On Windows (Command Prompt and PowerShell)

Command Prompt (cmd)

bat
set
set DEBUG
set DEBUG=true
python app.py
set DEBUG=

PowerShell

powershell
Get-ChildItem Env:
$Env:DEBUG
$Env:DEBUG = "true"
python app.py
Remove-Item Env:DEBUG

In real projects you usually:

Reading Environment Variables in Python

Python provides the os module to access environment variables.

Using `os.environ`

python
import os
# Read a variable, may raise KeyError if not set
database_url = os.environ["DATABASE_URL"]
print(database_url)

If DATABASE_URL is not set, this will raise a KeyError. Use this when a variable is required and your program cannot run without it.

Using `os.getenv`

python
import os
# Read a variable, return None if not set
debug = os.getenv("DEBUG")
print(debug)  # might be "true", "false", or None

You can also provide a default value:

python
debug = os.getenv("DEBUG", "false")  # default "false" if DEBUG is not set

This is useful for optional settings.

Rule: Use os.environ["NAME"] for required variables, and os.getenv("NAME", default) when you can fall back to a safe default.

Converting to Proper Types

Environment variables are always strings. You must convert them to the correct types yourself.

Converting to integer

python
import os
port_str = os.getenv("PORT", "8000")
port = int(port_str)  # "8000" -> 8000

Converting to boolean

You decide which strings count as true or false. For example:

python
import os
def str_to_bool(value: str) -> bool:
    return value.lower() in ("1", "true", "yes", "on")
debug_str = os.getenv("DEBUG", "false")
DEBUG = str_to_bool(debug_str)

Now DEBUG is a real bool.


Combining Environment Variables with Python Settings

In a backend project, you often create a dedicated module for configuration that reads environment variables once and exposes normal Python values.

For example, in config.py:

python
import os
def str_to_bool(value: str) -> bool:
    return value.lower() in ("1", "true", "yes", "on")
class Settings:
    def __init__(self) -> None:
        self.env = os.getenv("APP_ENV", "development")
        self.debug = str_to_bool(os.getenv("DEBUG", "true" if self.env == "development" else "false"))
        self.database_url = os.environ["DATABASE_URL"]  # required
        self.secret_key = os.environ["SECRET_KEY"]      # required
        self.port = int(os.getenv("PORT", "8000"))
settings = Settings()

Then elsewhere in your code:

python
from config import settings
print(settings.database_url)
print(settings.debug)

This pattern keeps configuration logic in one place and makes refactoring easier.


Using `.env` Files in Development

Manually exporting lots of environment variables can be annoying. Many Python projects use a simple text file named .env that contains key value pairs.

Example .env in the project root:

env
APP_ENV=development
DEBUG=true
DATABASE_URL=postgresql://user:pass@localhost:5432/app
SECRET_KEY=dev-secret-key
PORT=8000

This file is not automatically used by Python. You need a helper library to load it.

Using `python-dotenv`

Install:

bash
pip install python-dotenv

Then in your application entry point, for example main.py:

python
from dotenv import load_dotenv
import os
# Load variables from .env into the process environment
load_dotenv()
# Now you can access them via os.getenv or os.environ
database_url = os.environ["DATABASE_URL"]
debug = os.getenv("DEBUG", "false")
print(database_url, debug)

You can also load from a specific file:

python
load_dotenv(dotenv_path=".env.local")

Typical pattern:

Never commit .env files that contain real secrets. Add them to .gitignore so they are not stored in your repository.

Example .gitignore entry:

gitignore
.env
.env.local

Example: Simple FastAPI App with Environment Variables

Here is a small example that shows environment variables used in a minimal FastAPI app.

.env:

env
APP_ENV=development
DEBUG=true
PORT=8000
GREETING=Hello from environment

config.py:

python
from dotenv import load_dotenv
import os
load_dotenv()  # load from .env
class Settings:
    def __init__(self) -> None:
        self.env = os.getenv("APP_ENV", "development")
        self.debug = os.getenv("DEBUG", "false").lower() == "true"
        self.port = int(os.getenv("PORT", "8000"))
        self.greeting = os.getenv("GREETING", "Hello")
settings = Settings()

main.py:

python
from fastapi import FastAPI
from config import settings
app = FastAPI(debug=settings.debug)
@app.get("/")
def read_root():
    return {
        "environment": settings.env,
        "message": settings.greeting,
    }
if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="0.0.0.0", port=settings.port, reload=settings.debug)

Run:

bash
python main.py

Then change values in .env and restart the app to see different behavior without touching the Python code.


Environment Variables Across Environments

The same backend code will often run in three or more environments:

EnvironmentExample APP_ENVExample DEBUGExample DATABASE_URL
Developmentdevelopmenttruepostgresql://dev:dev@localhost:5432/dev
Stagingstagingfalsepostgresql://stag:stag@staging:5432/db
Productionproductionfalsepostgresql://prod:prod@db:5432/prod

You keep:

Typical workflow:

Common Pitfalls and Best Practices

Missing variables

If you blindly call os.environ["NAME"] and the variable is missing, your app will crash with KeyError.

A simple pattern:

python
import os
def require_env(name: str) -> str:
    value = os.getenv(name)
    if value is None:
        raise RuntimeError(f"Required environment variable {name} is missing")
    return value
DATABASE_URL = require_env("DATABASE_URL")

This gives a clearer error message and fails early.

Confusing strings with other types

Remember, everything is a string. Possible mistakes:

Be explicit:

python
debug_str = os.getenv("DEBUG", "false")
DEBUG = debug_str.lower() == "true"

Committing secrets

Never put values like real SECRET_KEY, DATABASE_URL with real passwords, or API keys into version controlled files.

Safer approaches:

Example .env.example:

env
APP_ENV=development
DEBUG=true
DATABASE_URL=postgresql://user:pass@localhost:5432/app
SECRET_KEY=change-me

Developers copy it:

bash
cp .env.example .env
# then edit .env with real local values

Summary

Environment variables are a small concept, but they are central to writing backend applications that are safe, configurable, and easy to deploy in different environments.

Views: 8

Comments

Please login to add a comment.

Don't have an account? Register now!