Build a layered config loader that merges settings from four sources — hardcoded defaults, a YAML or JSON config file, a .env secrets file, and runtime environment variable overrides — with explicit precedence at every layer: env vars win over .env, .env wins over the config file, and the config file wins over defaults. This pattern is used in virtually every production DevOps automation tool.
Create the lesson-304 directory, activate the virtual environment, and verify that PyYAML and python-dotenv are installed. Then create config.yaml (non-secret structured config), .env (secrets), and .gitignore. These three files together represent the three external config sources that the layered loader will merge.
mkdir -p ~/devops-python/lesson-304cd ~/devops-python/lesson-304source ~/devops-python/lesson-101/devops-env/bin/activatepip show pyyaml python-dotenv | grep -E 'Name|Version'# Non-secret deployment config — safe to commit to source control
environment: staging
api_base_url: https://api.staging.example.internal
api_timeout: 15
debug: false
max_retries: 5
deploy_host: deploy.staging.example.internal
deploy_port: 8080# Secrets — git-ignored, never commit this file
API_KEY=secret-key-abc123.env
.env.local
__pycache__/
*.pycecho '=== config.yaml ===' && cat config.yaml && echo && echo '=== .env ===' && cat .envThe three config sources have different trust levels and different use cases. config.yaml holds structured, non-secret settings that vary by environment — it is safe to commit and review in a pull request.
.env holds secrets (API keys, tokens, passwords) that must never reach a git repository.
Environment variables are injected at runtime by the platform (CI/CD, Docker, Kubernetes, systemd) and carry the highest precedence because they represent the live operating environment.
Separating these three sources is not just a convention: it is a security boundary.
Merging them in a single load_config() function gives the caller a clean Config object with no awareness of which source each value came from.
pip show prints Name: PyYAML and Name: python-dotenv with their versions. cat config.yaml shows the 7 non-secret config values. cat .env shows API_KEY=secret-key-abc123.
The lesson-304 directory contains config.yaml, .env, and .gitignore.