Preparing a Flask App for Production
Before deployment, ensure DEBUG is False (a debug-mode Flask app exposes the Werkzeug interactive debugger, which can execute arbitrary Python code if reached by an attacker), set a strong, randomly generated SECRET_KEY loaded from an environment variable rather than hardcoded, and move all secrets (database URLs, API keys) out of source control and into environment variables or a secrets manager.
Cricket analogy: A team never walks onto the field for a live match wearing training bibs and using a soft practice ball; leaving DEBUG=True in production is the equivalent of playing a real match with practice-mode safety nets still exposed.
Containerizing with Docker
A production Dockerfile typically starts from a slim Python base image, copies requirements.txt and installs dependencies before copying the rest of the source (to leverage Docker's layer caching), runs as a non-root user for security, and uses Gunicorn as the CMD instead of flask run. Multi-stage builds keep the final image small by discarding build tools like compilers that aren't needed at runtime.
Cricket analogy: A franchise ships a fully assembled, ready-to-play squad with kit and support staff to an away tour rather than expecting the host ground to provide everything, the same self-contained portability a Docker image provides an app.
Deployment Targets: VPS, PaaS, and Container Platforms
A raw VPS (like a DigitalOcean droplet) gives full control but requires you to manage the OS, Gunicorn, Nginx, TLS certificates (via Let's Encrypt/Certbot), and process supervision (systemd) yourself. A PaaS like Render, Railway, or Heroku handles most of that for you at the cost of some flexibility, while container platforms like AWS ECS, Google Cloud Run, or Kubernetes run your Docker image directly and handle scaling, but add operational complexity that's only worth it once traffic or team size justifies it.
Cricket analogy: A club can build and maintain its own ground with pitch curators and floodlights, or simply rent a stadium that already has all that infrastructure managed for them, mirroring the VPS-vs-PaaS tradeoff.
Environment Variables and Secrets Management
Twelve-factor app principles dictate that configuration varying between deploys (database URL, API keys, feature flags) belongs in environment variables, read in Flask via os.environ.get('DATABASE_URL'), never in code or committed config files. In production, these should come from your platform's secrets store (AWS Secrets Manager, Docker secrets, or a PaaS's environment variable dashboard) rather than a plaintext .env file sitting on the server.
Cricket analogy: A team's exact playing XI and batting order changes for every match based on conditions, decided fresh each time rather than baked into a fixed template, similar to environment-specific config being injected at deploy time.
Health Checks and Zero-Downtime Deploys
A /health or /healthz endpoint that checks database connectivity and returns 200 lets load balancers and orchestrators (Kubernetes, ECS) know an instance is ready to receive traffic and automatically restart it if it starts failing. Rolling deployments start new instances, wait for them to pass health checks, then drain and terminate old instances, avoiding the downtime a naive stop-then-start deploy would cause.
Cricket analogy: A twelfth man only comes onto the field once officially cleared by the umpires, and the injured player leaves only after the substitution is confirmed, mirroring how a rolling deploy waits for a health check before draining the old instance.
FROM python:3.12-slim AS base
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -m appuser && chown -R appuser /app
USER appuser
ENV FLASK_DEBUG=0
EXPOSE 8000
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "myapp:create_app()"]
Add a lightweight /health endpoint that only checks fast, critical dependencies (e.g. a quick db.session.execute('SELECT 1')) — an overly heavy health check can itself cause false-positive failures under load.
Never bake secrets like SECRET_KEY or database passwords into a Docker image layer, even temporarily during build — anyone with access to the image can extract them from its layer history.
- Set DEBUG=False and load SECRET_KEY from an environment variable before deploying, never hardcode it.
- Docker images should install dependencies before copying source code to maximize layer-cache reuse.
- Run containers as a non-root user and use Gunicorn, not flask run, as the production CMD.
- Choose between a raw VPS, a PaaS, or a container platform based on your team's operational capacity.
- Twelve-factor config means environment-specific values live in environment variables, not source code.
- A /health endpoint lets load balancers and orchestrators route traffic only to ready instances.
- Rolling deployments avoid downtime by starting and health-checking new instances before draining old ones.
Practice what you learned
1. Why is running Flask with DEBUG=True in production a serious security risk?
2. In a Dockerfile, why copy requirements.txt and install dependencies before copying the rest of the source code?
3. According to twelve-factor app principles, where should a database connection URL that differs between staging and production be stored?
4. What is the primary purpose of a /health endpoint in a production deployment?
5. What distinguishes a rolling deployment from a naive stop-then-start deployment?
Was this page helpful?
You May Also Like
Flask with Gunicorn and WSGI
Understand the WSGI standard that powers Flask and how to run Flask applications in production using Gunicorn as an application server.
Testing Flask Applications
Learn how to write reliable unit and integration tests for Flask apps using pytest, the Flask test client, and fixtures for database and app context.
Flask Quick Reference
A condensed cheat sheet covering Flask routing, request/response handling, context objects, and CLI essentials for quick lookup while coding.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics