This episode covers secure application configuration: Twelve-Factor config through environment variables, loading .env with python-dotenv, tools like dynaconf and pydantic settings, and best practices for managing secrets so they never get committed to Git.

Configuration and secrets are the most often overlooked but most critical parts. Episode 11 covers how to manage application configuration correctly: separating configuration from code, loading it from the environment, and protecting secrets so they never leak into Git.
We'll learn Twelve-Factor config, python-dotenv, modern tools like dynaconf and pydantic-settings, and the best practices adopted by production teams around the world.
The Twelve-Factor App is a methodology for building modern applications. Its third principle states: store configuration in the environment. Configuration is everything that differs between deployments — database URLs, credentials, and feature toggles.
import os
host = os.getenv("DATABASE_HOST", "localhost")
port = os.getenv("DATABASE_PORT", "5432")
print(host, port)os.getenv("DATABASE_HOST", "localhost") reads an environment variable with a default value. Configuration held in the environment can change without changing code — the foundation of flexible deployment.
Storing configuration in code causes problems: code can't be reused across environments, and secrets get committed to Git along with it. With the environment, dev and production configuration differ without changing a single line of source code.
During local development, typing export over and over is impractical. python-dotenv loads a .env file into the environment:
pip install python-dotenvfrom dotenv import load_dotenv
import os
load_dotenv()
database_url = os.getenv("DATABASE_URL")
print(database_url)load_dotenv() reads the .env file in the working directory and puts its contents into the environment. After that os.getenv just works. This speeds up local setup without sacrificing consistency.
A .env file contains key-value pairs:
DATABASE_URL=postgresql://user:pass@localhost/db
API_KEY=secret-anda-jangan-di-commit
LOG_LEVEL=INFOThis .env file must be in .gitignore — we'll cover how in the secrets section. Its contents are specific to each developer and must not be shared through Git.
dynaconf provides layered configuration with support for many formats:
pip install dynaconffrom dynaconf import Dynaconf
settings = Dynaconf(
settings_files=["settings.toml", ".env"],
envvar_prefix="APP",
)
print(settings.host)
print(settings.get("port", 8000))Dynaconf(settings_files=["settings.toml", ".env"]) loads configuration from several sources with priority. envvar_prefix="APP" means environment variables like APP_PORT are also recognized. dynaconf suits applications with many configuration layers.
pydantic-settings combines pydantic with the environment:
pip install pydantic-settingsfrom pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str
database_url: str
debug: bool = False
model_config = SettingsConfigDict(env_file=".env")
settings = Settings()
print(settings.app_name)class Settings(BaseSettings) defines configuration with types and automatic validation. model_config points to the .env file. If database_url is missing or the wrong type, the application fails at startup — much better than failing halfway through.
The first rule of secret management: never commit them to Git:
.env
*.env
!.env.exampleThe .env and *.env lines exclude secret files from Git. The exception !.env.example still allows a template without secret values to be committed. This is the standard practice in all professional Python projects.
Commit an empty template so the team knows which variables are needed:
DATABASE_URL=postgresql://user:pass@localhost/db
API_KEY=ganti-dengan-key-anda
LOG_LEVEL=INFOThe .env.example file contains variable names with example or placeholder values. New team members just copy it to .env and fill in real values. This template is safe to commit because it holds no secrets.
In production, secrets should be managed by dedicated systems, not in files:
The golden rule: secrets are injected at runtime, never written in code or images. You'll practice this during deployment in episode 20.
Key takeaways:
In the next episode, episode 12, we'll cover basic networking and HTTP clients — modern usage of requests, httpx for sync and async, setting up retries, timeouts, and connection pooling, plus an overview of WSGI servers with Gunicorn and ASGI with Uvicorn. Your project starts communicating with the outside world!