Deployment

Every earlier lesson ran Bookshelf, conceptually, with flask run or app.run(debug=True) — perfect for development, and completely wrong for serving real traffic. This lesson covers what actually changes between "runs on my machine" and "runs in production," in a way that applies no matter which hosting platform Bookshelf eventually lands on.

Why the Dev Server Isn't for Production

Flask's built-in development server exists purely to make local development convenient — live reloading, a helpful debugger page on errors, no separate process to configure. Flask itself is explicit that it was never meant to run production traffic: starting it prints its own warning to that effect. Concretely, it's single-threaded by default (one request is handled at a time), and it isn't hardened against the kind of malformed or hostile requests a server exposed to the public internet inevitably receives.

A Real WSGI Server: Gunicorn

Production Flask apps run behind a proper WSGI application server instead — software built specifically to run a WSGI app (which is exactly what a Flask app is) reliably, handling many requests concurrently. Gunicorn is the most common choice for Flask on Linux-based hosting.

pip install gunicorn
Note: Gunicorn doesn't run natively on Windows. Waitress is a solid pure-Python alternative that works the same way and runs fine on Windows, if that's the development or hosting environment in question.

The Production Request Path

A typical production setup adds one more piece in front of Gunicorn: a reverse proxy like Nginx. Understanding the shape of this, conceptually, matters more than any single command:

Browser request → Nginx (reverse proxy) → Gunicorn (WSGI server) → your Flask app

Nginx sits at the front, receiving every incoming request first. For most requests, it simply forwards them along to Gunicorn, which runs Bookshelf's actual Flask code. But Nginx also takes on one job directly, without ever involving Flask at all: serving static files — exactly the CSS, JavaScript, and images covered back in Static Files & Assets. A web server built for exactly this is dramatically faster at serving a plain file than routing that same request through Gunicorn and Flask just to hand back a CSS file unchanged.

The Pre-Deployment Checklist

Regardless of where Bookshelf ultimately gets hosted, this same checklist applies every time:

ItemWhy it matters
debug=FalseFlask's debugger, if left on, can expose source code and even allow arbitrary code execution to anyone who can trigger an error page — see Security Best Practices
A real SECRET_KEY from the environmentNever a hardcoded string committed to source control — sessions and CSRF tokens depend on it staying secret
FLASK_ENV=productionEnsures Flask and its extensions behave with production-appropriate defaults rather than development ones
Migrations appliedflask db upgrade run against the production database before traffic hits it — see Migrations with Flask-Migrate

Running with Gunicorn

Assuming Bookshelf uses the app factory pattern, Gunicorn is pointed at the factory call itself:

gunicorn -w 4 'app:create_app()'

-w 4 tells Gunicorn to start 4 separate worker processes, each capable of handling requests independently and in parallel — a rough starting point is commonly (2 × CPU cores) + 1, tuned from there based on actual load. This is also exactly why a shared cache backend like Redis matters, as covered in Caching: each of these 4 workers is a completely separate process with its own memory.

Where Flask Apps Actually Get Hosted

There's no single correct hosting platform — these are the three broad, legitimate paths people actually take, each with real tradeoffs:

ApproachTradeoff
A self-managed VPS/cloud serverFull control over Gunicorn, Nginx, and every setting — but you own patching, monitoring, and every piece of the setup yourself
A PaaS (Railway/Render/Heroku-style platforms)Handles the server, the process management, and often TLS certificates for you — less control, but dramatically less to set up and maintain
Containerizing with DockerAn increasingly standard middle ground — package the app once, run the same image consistently across a VPS, a PaaS, or a container-orchestration platform

All three are legitimate, widely used choices for a real Flask app — which one fits best depends on how much operational control is worth taking on versus handing off, not on any of them being objectively "more correct" than the others.

Next up: Best Practices closes out this entire track with a ten-point checklist pulling together the habits worth carrying forward from every lesson so far.