

Photo by author
# Introduction to Secret Keeping
Storing sensitive information such as API keys, database passwords, or tokens directly in your Python code is dangerous. If these secrets are leaked, attackers can breach your system, and your organization can suffer loss of trust, financial and legal consequences. Instead, you should externalize secrets so that they are never exposed to code or version control. It is a common best practice to store secrets in environment variables (outside your code). This way, the secrets are never exposed in the codebase. Although, manual environment variables do work, it’s easier to keep all the secrets in a single .ENV file for local development.
This article explains Seven practical techniques for managing secrets in Python projectswith code examples and descriptions of common pitfalls.
# Technique 1: Using the .ENV file locally (and loading it safely)
a .env file is a text file key = value Pairs that you keep locally (not in version control). It lets you specify environment-specific settings and development secrets. For example, a suggested project configuration is:
my_project/
app/
main.py
settings.py
.env # NOT committed – contains real secrets
.env.example # committed – lists keys without real values
.gitignore
pyproject.tomlYour real secrets go inside .env Locally, such as:
# .env (local only, never commit)
OPENAI_API_KEY=your_real_key_here
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
DEBUG=trueOn the contrary, .env.example Here’s a template you’ve committed, for other developers to see which keys are needed:
# .env.example (commit this)
OPENAI_API_KEY=
DATABASE_URL=
DEBUG=falseAdd samples to ignore these files in git:
So that your secret .ENV is never accidentally checked in. In Python, the common practice is to use Python-Dotino library, which will load .env file at runtime. For example, I app/main.p You can write:
# app/main.py
import os
from dotenv import load_dotenv
load_dotenv() # reads variables from .env into os.environ
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("Missing OPENAI_API_KEY. Set it in your environment or .env file.")
print("App started (key loaded).")here, load_dotino() It is found automatically .env in the working directory and sets each key = value i OS.ENVIRON (unless this variable is already set). This approach avoids common mistakes like committing the .ENV or sharing it unsafely, while giving you a clean, reproducible development environment. You can switch between machines or dev setups without changing code, and local secrets are preserved.
# Technique 2: Read secrets from the environment
Some developers like placeholders api_key = “test” In their code or assume that variables are always set in increments. It may work on their machine but fails in production. If a secret is missing, the placeholder is running and may pose a security risk. Instead, always fetch secrets from environment variables at runtime. In Python, you can use OS.ENVIRON or os.getenv To retrieve values ​​safely. For example:
def require_env(name: str) -> str:
value = os.getenv(name)
if not value:
raise RuntimeError(f"Missing required environment variable: {name}")
return value
OPENAI_API_KEY = require_env("OPENAI_API_KEY")This makes your app fail faster on startup if a secret is missing, which is much safer than proceeding with a missing or dummy value.
# Technique 3: Verify the configuration with the settings module
As projects grow, many are fragmented os.getenv Calls become messy and error-prone. Using the Settings class Pedantic’s base settings Sets configuration, validates types, and loads .ENV and environment-specific values. For example:
# app/settings.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
openai_api_key: str = Field(min_length=1)
database_url: str = Field(min_length=1)
debug: bool = False
settings = Settings()Then in your app:
# app/main.py
from app.settings import settings
if settings.debug:
print("Debug mode on")
api_key = settings.openai_api_keyThis prevents errors such as mistyping keys, misunderstanding types (“false” vs. false), or duplicating environments. Using the settings class ensures that your app fails quickly if your secrets are missing and avoids “works on my machine” problems.
# Technique 4: Using Platform/CI Secrets for Deployments
When you deploy to production, you should not copy your local .env File Instead, use your hosting/CI platform’s secret management. For example, if you’re using GitHub Actions for CI, you can store encrypted secrets in repository settings and then inject them into workflows. That way, your CI or cloud platform injects the actual values ​​at runtime, and you never see them in code or logs.
# Technique 5: Docker
In Docker, avoid backing secrets into images or using plain env. Docker and Kubernetes provide mechanisms for secrets that are more secure than environment variables, which can be leaked through process listings or logins. For local dev, .ENV plus python-dotinv works, but in production containers, use secret or docker-secret. avoid env api_key = … Committing compose files to docker files or with secrets. By doing this, the risk of secrets is permanently exposed in the images and facilitates circulation.
# Technique 6: Adding Guards
Humans make mistakes, so automate privacy protection. GitHub Push Protection can prevent contracts containing secrets, and CI/CD secret scanning tools such as TruffleHog or GitLeaks detect leaked credentials before they are found. Beginners often rely on memory or speed, which leads to accidental agreements. Guardrails prevent leaks before they enter your repo, making it safer to work with .ENV and environment variables in development and deployment.
# Technique 7: Using a Real Secret Manager
For large applications, it makes sense to use a suitable secrets manager such as HashCorp Vault, AWS Secret Manager, or Azure Key Vault. These tools control who can access secrets, log every access, and rotate keys automatically. Without one, teams often reuse passwords or forget to rotate them, which is a risk. A secret manager keeps everything under control, simplifies rotation, and protects your production system even if a developer’s computer or local .env The file is exposed.
# wrap up
Keeping secrets is more than just following the rules. It’s about building a workflow that makes your projects secure, easy to maintain, and portable across environments. To make it easier, I’ve put together a checklist that you can use in your Python projects.
- .env i am .gitignore (never commit real credentials)
- .env.example exists and is determined with empty values
- The code reads Secret Only through environment variables (os.getenv, a settings class, etc.)
- App Fails fast with a clear error if a required secret is missing
- You use Different secrets For dev, staging, and prod (never reuse the same key)
- Use CI and deployment Encrypted secrets (GitHub Action Secret, AWS Parameter Store, etc.)
- Push protection and/or secret scanning is enabled on your device
- You have one Circulation Policy (If leaking and regularly otherwise rotate keys immediately)
Kanwal Mehreen is a machine learning engineer and technical writer with a deep passion for data science and the intersection of AI with medicine. He co-authored the eBook “Maximizing Productivity with ChatGPT.” As a 2022 Google Generation Scholar for APAC, she champions diversity and academic excellence. He has also been recognized as a Teradata Diversity in Tech Scholar, a MITACS GlobalLink Research Scholar, and a Harvard Wicked Scholar. Kanwal is a passionate advocate for change, having founded the Fame Code to empower women in stem fields.