The Quiet Terminal

← Practical Tips

Keeping secrets out of your code with .env files

2026-07-24 · 4 min read

The first weather script I wrote had the API key right there on line 4. It worked. It also went straight into a "private" repo, and the key was dead within a day — scraping bots find API keys in public repos within minutes, and mine was only private until I made it public to share the code. Deleting the line wasn't enough, either: the key lived in the git history forever, so it had to be revoked, not just removed.

The fix is to treat keys like configuration, not code: they change per machine and per project, so they don't belong next to the lines that never change. The standard setup is a .env file, ignored by git, plus a tiny helper that loads it.

The file

WEATHER_API_KEY=9f3ab2c47e
DEBUG=0

Plain text, no quotes needed, no spaces around the =. One variable per line. The file lives in the project's root, next to the code that reads it.

Loading it

import os
from dotenv import load_dotenv

load_dotenv()   # reads .env in the current directory into os.environ

key = os.environ.get("WEATHER_API_KEY")
if key is None:
    raise SystemExit("WEATHER_API_KEY is not set — copy .env.example to .env")

pip install python-dotenv, call load_dotenv() once at the top, and from then on the variables read like any other environment variable. I like that the code never knows or cares where the value came from — on a server there's no .env file at all, and the same lines work because the variable is set in the environment instead.

os.environ.get() takes a default for anything optional, which keeps small scripts runnable on a fresh machine:

debug = os.environ.get("DEBUG", "0") == "1"

Two gotchas worth knowing. load_dotenv() by default does not override variables already set in the real environment — handy on a server, confusing the one time a stale variable shadows your file; pass override=True if you want the file to win. And every so often, run git status and confirm .env shows up as untracked. The day it stops appearing there is the day to stop and look closely.

Two files, one habit

Add .env to .gitignore on day one, before the first commit:

.env

And commit a sibling file, .env.example, with the names but not the values:

WEATHER_API_KEY=
DEBUG=0

That example file is the part people skip, and it's the part I'd keep if I could only keep one. It's the shopping list for the next machine: without it, you rediscover your own variable names one runtime error at a time, on a laptop where the script worked perfectly two months ago. And if a key ever does get committed, remember the lesson at the top of this post — remove the line, then rotate the key, because history keeps what HEAD forgets.