Imagine you wrote a program with an API key hardcoded in the source and pushed it to GitHub. An hour later you get a notification — bots found the key and made 10,000 requests on your behalf. Your account is blocked.
This is a real story. It happens all the time.
The Rule: Secrets Don’t Live in Code
Never store in code:
- API keys
- Database passwords
- Auth tokens
- Application secret keys
Why it’s critical:
- Git stores the entire history — even if you delete the key from the latest commit, it stays in history
- A public repository = accessible to the entire world
- Bots scan GitHub 24/7 looking for keys
The Solution: .env File
.env is a text file containing environment variables. It lives only locally and never goes into git.
# .env
DATABASE_URL=postgresql://user:password@localhost/mydb
API_KEY=abc123def456ghi789
SECRET_KEY=very-secret-key
DEBUG=True
Format: NAME=value, no spaces around =.
python-dotenv: Loading .env into Python
pip install python-dotenv
from dotenv import load_dotenv
import os
load_dotenv() # loads variables from .env into os.environ
api_key = os.getenv("API_KEY") # "abc123def456ghi789"
debug = os.getenv("DEBUG", "False") # second argument — default value
load_dotenv() must be called before the first os.getenv().
.gitignore: Protecting .env
Add .env to .gitignore — then git won’t see it at all:
# .gitignore
.env
.env.local
.env.production
.venv/
__pycache__/
Verify the file is ignored:
git status # .env should not appear in the list
Multiple Environments
Often you need different settings for development and production:
.env # local development (in .gitignore)
.env.example # template without values (committed to git!)
.env.production # production (on the server, not in git)
The .env.example file shows which variables are needed, but without real values:
# .env.example
API_KEY=
DATABASE_URL=
SECRET_KEY=
A new developer clones the repo, copies .env.example to .env, and fills in their own values.
Passing Secrets to a Server
On a production server, a .env file isn’t needed — variables are set differently:
# Heroku:
heroku config:set API_KEY=abc123
# Railway / Render / other hosting:
# Via the web interface under Environment Variables
# Kubernetes:
kubectl create secret generic my-secret --from-literal=API_KEY=abc123
The code stays the same — os.getenv("API_KEY") works everywhere.
Common Mistakes
os.getenv Returns None
# Wrong — load_dotenv called after os.getenv:
api_key = os.getenv("API_KEY") # None
load_dotenv()
# Correct:
load_dotenv()
api_key = os.getenv("API_KEY") # value from .env
Spaces Around = in .env
API_KEY = abc123 # ← wrong, python-dotenv won't parse it
API_KEY=abc123 # ← correct
File Named env Instead of .env
The leading dot is required: .env is a hidden file on Unix systems.
Summary
The pattern is simple: all secrets in .env, .env in .gitignore, only os.getenv() in code. Following this rule is enough to never leak keys through git.
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!