Environment Variables
Configure apps with environment variables — the standard for secrets, config, and runtime settings
Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.
UpgradeKnowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
Environment variables are key-value pairs available to a process and all its children. They are the standard way to configure apps differently across environments (dev, staging, prod) without changing code.
Viewing and setting variables:
bash env # list all environment variables printenv HOME # print a specific variable echo $HOME # same — $ expands the variable echo $PATH # the directories searched for executables # Set in current shell (not persistent) export DATABASE_URL="postgresql://localhost/mydb" export DEBUG=true # Set for a single command (does not pollute the shell) DEBUG=true python app.py NODE_ENV=production node server.js
PATH — how the shell finds commands:
bash echo $PATH # /usr/local/bin:/usr/bin:/usr/sbin:/home/bill/.local/bin # Add a directory to PATH export PATH="$PATH:/opt/my-tools/bin" # Verify a command will be found which python3 # /usr/bin/python3
Making variables persistent — shell config files:
bash # For interactive shells: ~/.bashrc (bash) or ~/.zshrc (zsh) echo 'export EDITOR=vim' >> ~/.bashrc echo 'export GOPATH=$HOME/go' >> ~/.bashrc source ~/.bashrc # reload without restarting terminal # For login shells: ~/.bash_profile or ~/.profile # Rule of thumb: put exports in ~/.bashrc (most configs work there)
The .env file pattern (12-factor apps):
bash # .env file — never commit to git! DATABASE_URL=postgresql://localhost/mydb SECRET_KEY=supersecretvalue STRIPE_KEY=sk_live_abc123 DEBUG=false # Load with Python-dotenv # from dotenv import load_dotenv; load_dotenv() # Load with bash set -a; source .env; set +a # set -a marks all subsequent assignments for export # Always add .env to .gitignore echo ".env" >> .gitignore
Variable expansion:
bash NAME="Bill" echo "Hello, $NAME" # Hello, Bill echo "Home: ${HOME}" # use braces for clarity with adjacent text echo "${NAME:-default}" # use "default" if NAME is unset or empty echo "${NAME:?error msg}" # error and exit if NAME is unset (great in scripts)
Examples
Configuring a Flask app with environment variables
os.environ["KEY"] raises KeyError if missing (good — fail fast). os.getenv("KEY", default) returns the default if missing.
# .env (never commit this!)
FLASK_ENV=development
DATABASE_URL=postgresql://bill:secret@localhost/myapp
SECRET_KEY=dev-only-secret-do-not-use-in-prod
REDIS_URL=redis://localhost:6379
# app.py — read from environment, not hardcoded
import os
from dotenv import load_dotenv
load_dotenv() # reads .env file into os.environ
DATABASE_URL = os.environ["DATABASE_URL"] # fails fast if missing
DEBUG = os.getenv("DEBUG", "false").lower() == "true" # optional with default
# In production (Heroku, Railway, Vercel) — set via dashboard
# No .env file needed — the platform injects the variables
# Never do this:
# DATABASE_URL = "postgresql://bill:secret@prod-db/myapp" — hardcoded secret!Managing PATH and persistent exports
Prefix-style KEY=value command is clean for one-off overrides — CI pipelines use this pattern heavily.
# See the current PATH
echo $PATH
# /usr/local/bin:/usr/bin:/usr/sbin:/home/bill/.local/bin
# Install a tool to ~/bin and add it to PATH
mkdir -p ~/bin
cp my-script.sh ~/bin/my-script
# Add ~/bin to PATH permanently
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Verify it works
which my-script
# /home/bill/bin/my-script
my-script
# Set a variable for one command only (does not affect shell)
DATABASE_URL=postgresql://test-db python -m pytest
# Debug: see what a child process will receive
env DATABASE_URL=test python -c "import os; print(os.environ['DATABASE_URL'])"
# testHow well did you understand this?
Next in Linux & Command Line
Cron Jobs