Deployment

Every single example throughout this track has assumed you're running TaskFlow with python manage.py runserver. That command has done its job well for local development, but it was never built to survive contact with the real internet. This lesson is about the gap between "runs on my machine" and "runs reliably for real users" — and it's deliberately written to apply regardless of which cloud provider or hosting platform you eventually choose.

Why runserver Isn't a Production Server

Django's development server is single-threaded, handles one request at a time by default, reloads code automatically on every change, and displays detailed debug information when something breaks — every one of those traits is exactly what you want while developing, and exactly what you don't want in production. A real production deployment needs a proper application server in its place.

For a traditional, synchronous Django app, Gunicorn is the most common choice — a WSGI server built to run Python web applications under real traffic, handling multiple requests concurrently across several worker processes:

gunicorn taskflow_project.wsgi:application

If TaskFlow leans on Django's newer async capabilities instead, an ASGI server like Uvicorn or Daphne fills the equivalent role — worth knowing exists, though this track has stayed on the traditional synchronous path throughout.

A Typical Production Request Path

Gunicorn on its own is usually not exposed directly to the internet either. The conventional setup puts a reverse proxy — commonly Nginx — in front of it:

ComponentRole
Nginx (reverse proxy)Receives every incoming request first. Forwards anything meant for your Django app on to Gunicorn.
GunicornRuns your actual Django application — the views, templates, and ORM code built throughout this track.
Nginx (static files)Also serves static files (CSS, JS, images) directly, without ever routing those requests through Django or Gunicorn at all — exactly the production setup described in Serving Files in Production.

Splitting the work this way plays to each tool's strengths — Nginx is extremely efficient at serving static files and handling raw connections, while Gunicorn focuses purely on running your Python application code.

The Pre-Deployment Checklist

Regardless of where TaskFlow eventually gets hosted, a short list of things needs to be true before it goes live:

Setting / stepWhy it matters
DEBUG = FalseDjango's debug pages reveal a huge amount of internal detail — settings, stack traces, source code snippets. None of that belongs in front of real users.
A real SECRET_KEY from the environmentRead from an environment variable exactly as covered in Environment Configuration — never a placeholder, never hardcoded.
ALLOWED_HOSTSMust list your actual production domain(s) — Django refuses requests with a Host header that isn't on this list, as a defense against certain HTTP attacks.
python manage.py collectstaticGathers every app's static files into one directory Nginx (or your platform's equivalent) can serve directly.
Migrations appliedpython manage.py migrate run against the actual production database — not just your local one.

Let Django Audit Itself: check --deploy

Remembering every production-readiness setting by hand is exactly the kind of thing worth automating, and Django already ships a command that does it. Running the built-in check with the --deploy flag audits your current settings against a checklist of common production misconfigurations and prints a warning for anything it finds:

python manage.py check --deploy

It flags things like an insecure SECRET_KEY, missing security headers, cookies not marked secure, and DEBUG left on — a genuinely underrated tool worth running as a standard step before every single production deploy, not just the first one.

Where People Actually Deploy Django

There's no single correct hosting choice — TaskFlow could reasonably run on any of these, with different tradeoffs:

OptionTradeoff
A VPS / cloud server you manage yourselfFull control over Gunicorn, Nginx, and the OS itself — but you're also responsible for configuring, securing, and maintaining every one of those pieces yourself.
A PaaS (Railway, Render, Heroku-style platforms)Handles the server, process management, and often the reverse proxy for you — you mostly push code and set environment variables. Less control, considerably less operational burden.
Docker containersAn increasingly standard middle ground — you define the exact environment your app needs once, in a Dockerfile, and that same container can run consistently on a VPS, a PaaS that supports containers, or a full orchestration platform.

All three are legitimate, widely-used choices in real production Django projects. Which one fits best depends on how much operational control you want versus how much you'd rather hand off to a platform.

Note: Every command and setting above reflects genuine, correct production Django practice — but this website's own project doesn't run Gunicorn, Nginx, or a live deployment, so nothing here executes on this page.
Next up: Best Practices closes out the entire track with a checklist pulling together the most important habits from everything covered so far.