Module 09 / Project 05 — Production Config¶
Home: README
Learn Your Way¶
| Read | Build | Watch | Test | Review | Visualize | Try |
|---|---|---|---|---|---|---|
| — | This project | — | — | Flashcards | — | — |
Focus¶
Environment variables, secrets management, health checks, structured logging, CORS, graceful shutdown, and non-root container users.
Why this project exists¶
Building a Docker image is not the same as being production-ready. Production applications need proper configuration management (so secrets stay out of source code), structured logging (so you can debug issues in running containers), health checks (so orchestrators can restart failed containers), and security hardening (so a compromised container has minimal privileges). This project puts all those pieces together.
Run¶
Local development¶
cd projects/modules/09-docker-deployment/05-production-config
# Run with default settings (SQLite, debug logging)
DEBUG=true LOG_LEVEL=DEBUG python app.py
With Docker Compose (recommended)¶
# Start the app and database
docker compose up --build
# In another terminal, check health status
docker compose ps
Verify the endpoints¶
# Root endpoint — app metadata
curl http://127.0.0.1:8000/
# Health check — includes database status
curl http://127.0.0.1:8000/health
# Configuration — non-sensitive settings
curl http://127.0.0.1:8000/config
Watch the logs¶
You will see structured log output like:
2024-01-15 14:30:00 | production-app | INFO | Starting production-app v1.0.0
2024-01-15 14:30:00 | production-app | INFO | Debug mode: False
2024-01-15 14:30:00 | production-app | INFO | Database tables verified
Expected output¶
GET / returns:
GET /health returns:
GET /config returns:
{"app_name": "production-app", "version": "1.0.0", "debug": false, "log_level": "INFO", "workers": 2}
Production deployment checklist¶
Before deploying to production, verify each item:
- All secrets come from environment variables, not source code.
-
.envis in.gitignore(never committed). -
DEBUG=falsein production. -
LOG_LEVEL=INFOorWARNINGin production. -
ALLOWED_ORIGINSis restricted to your frontend domain(s). - The container runs as a non-root user.
- Health checks are configured in the Dockerfile and docker-compose.yml.
- Restart policy is set (
unless-stoppedoron-failure). - Resource limits are set (CPU and memory).
- Database credentials are unique per environment.
Alter it¶
- Add a
GET /readyendpoint (separate from/health) that returns 503 if the database is not connected. This is a "readiness probe" used by Kubernetes. - Add a
REQUEST_TIMEOUTsetting toconfig.pyand log it at startup. - Change
LOG_LEVELtoDEBUGin docker-compose.yml and observe the additional log output.
Break it¶
- Remove the
DATABASE_URLenvironment variable from docker-compose.yml. What happens when the app tries to connect to the database? - Change
USER appusertoUSER rootin the Dockerfile. The app still works, but explain why this is a security risk. - Remove the
healthcheckfrom the db service and thecondition: service_healthyfrom the web service. Start the stack. Does the web service crash because the database is not ready?
Fix it¶
- Restore the
DATABASE_URLenvironment variable. Without it, the app falls back to SQLite (the default in config.py), which is not what you want in production. - Restore
USER appuser. Running as root inside a container gives an attacker full control if they exploit a vulnerability. Non-root users limit the damage. - Restore the health check and condition. Without them, the web service may start before PostgreSQL is ready, causing connection errors on the first few requests.
Explain it¶
- Why should configuration come from environment variables instead of hardcoded values?
- What is the difference between a health check and a readiness check?
- Why does the Dockerfile create a non-root user? What is the security risk of running as root?
- What does
restart: unless-stoppeddo? How is it different fromrestart: always? - Why does the
.env.examplefile exist alongside.env? Why is.envnot committed?
Mastery check¶
You can move on when you can:
- configure a FastAPI application entirely through environment variables,
- explain why secrets must never appear in source code,
- set up Docker health checks and explain their purpose,
- describe why containers should run as non-root users,
- read structured logs and use them to diagnose issues.
Related Concepts¶
- Files and Paths
- The Terminal Deeper
- Types and Conversions
- Virtual Environments
- Quiz: Files and Paths
Next¶
You have completed Module 09 — Docker & Deployment. You now have the skills to containerize any Python application and deploy it with confidence. Consider continuing to Module 10 — Django Full-Stack or Module 12 — Cloud Deployment.