Never run the development server in production
The built-in development servers in Python frameworks are single-threaded, unoptimised and explicitly not intended for production. They are also frequently found running production systems.
Behind a production application server, with a reverse proxy in front and a process supervisor keeping it alive. That is the minimum viable arrangement.
The deployment stack
- A production application server running the Python code
- A reverse proxy handling TLS, static files and buffering
- A process supervisor restarting anything that exits
- Separate worker processes for background tasks
- Log output collected rather than written to a file nobody reads
Configuration from the environment
- No credentials in the repository, ever
- Different configuration per environment without code changes
- Configuration validated at startup, failing fast if something is missing
- Secrets managed properly rather than in a plain file
An application that starts successfully with a missing configuration value and fails at three in the morning is a preventable problem.
Deploy from version control
| Requirement | Why |
|---|---|
| Deploy a specific commit | You know what is running |
| Dependencies installed at deploy | Reproducible |
| Migrations run in order | Schema matches code |
| Atomic switch | No half-deployed state |
| Tested rollback | Recovery is routine |
Health checks and graceful shutdown
A health endpoint lets monitoring and load balancers know whether the service is actually working, not just running. It should check dependencies rather than always returning success.
Graceful shutdown — finishing in-flight requests before exiting — means deployments do not drop work. Both are small pieces of work with disproportionate operational value.