Deployment
Website Rebuild with Django
Chapter 11 · Deployment
Chapter 5 promised this moment would come back around: flip DEBUG to False, and static files quietly stop being served. This chapter is where that promise gets kept, alongside everything else a real production deployment actually needs — reusing the same lessons Food Tracker (Django) 12 already worked through, and landing on a genuinely different deployment story than Website Rebuild with Next.js 11's own Vercel-or-self-hosted split.
manage.py runserver Was Never for Production
Every chapter so far has used runserver — and Django's own documentation is explicit that it was never meant to handle real production traffic: no real concurrency, no process management, not hardened for the open internet.
gunicorn: Django's Own Production WSGI Server
gunicorn is a real WSGI application server — multiple worker processes, genuine concurrency, built to sit behind a proper reverse proxy rather than face the internet directly.
DEBUG=False: Two Things Happen at Once
The first is safety: DEBUG=True shows a detailed error page on any unhandled exception — full traceback, source code, local variable values, even settings — genuinely dangerous information to hand any random visitor who happens to trigger an error. The second, per Chapter 5, is that Django's own automatic static file serving stops working entirely the moment DEBUG=False — two unrelated-sounding consequences from one single setting.
ALLOWED_HOSTS: Required the Moment DEBUG=False
Django refuses any request whose Host header isn't in this list — a real defense against HTTP Host-header attacks, and a genuinely common "why is production suddenly returning 400 Bad Request" surprise for anyone who forgets to set it before their first production deploy.
SECRET_KEY: Never the Auto-Generated Dev Value
SECRET_KEY signs session cookies and every CSRF token Chapter 8's own {% csrf_token %} generates — real security infrastructure, not a cosmetic setting, and it belongs in an environment variable, never hardcoded and committed to a repository alongside the code.
SECRET_KEY is what actually signs session data, changing it invalidates every existing session at once — including Chapter 9's own imported admin account. This isn't a bug to chase down; it's a direct, expected consequence of what the key actually does. A surprise "why am I suddenly logged out" moment the first time it happens, not a sign anything went wrong.
Finally Resolving Chapter 5's Own Forward Reference
nginx as Reverse Proxy
nginx serves everything under /static/ directly from STATIC_ROOT — no Python involved at all for a plain file — and forwards everything else to gunicorn. The same reverse-proxy pattern this site's own Web Servers Fundamentals and Nginx In Depth courses already cover in real depth, applied here rather than re-taught from scratch.
No Django-Specific Zero-Config Platform
| Next.js | Django | |
|---|---|---|
| Zero-config platform | Vercel — built by the same team, for this exact framework | None — no Django-specific equivalent exists |
| Self-hosted path | PM2 + nginx, covered as the alternative to Vercel | gunicorn + nginx — the only real path shown here |
Hands-On Exercises
Explain why manage.py runserver is explicitly not meant for production use, and what gunicorn provides that it doesn't.
Explain the two separate things that happen the moment DEBUG=False is set, and why leaving DEBUG=True active in production would be a real security problem.
Explain why rotating SECRET_KEY immediately logs out every currently-logged-in admin, including the account imported back in Chapter 9.
Chapter 11 Quick Reference
gunicorn— the real production WSGI server;runserveris explicitly dev-onlyDEBUG=False— disables both the detailed error pages (a real information leak) and automatic static file serving (Chapter 5's own forward reference)ALLOWED_HOSTS— required onceDEBUG=False; rejects unrecognizedHostheadersSECRET_KEY— signs sessions and every CSRF token; belongs in an environment variable, never hardcoded- Rotating
SECRET_KEYlogs everyone out — a direct, expected consequence, not a bug collectstatic— gathers every app's static files intoSTATIC_ROOT, finally resolving Chapter 5's own forward reference- nginx + gunicorn — the same reverse-proxy pattern this site's own Web Servers courses already teach in depth
- No Vercel-equivalent for Django — a genuine, honest structural difference, not a gap in this course's own coverage
- Next chapter: Capstone — A Complete, Working Flexible-Routing Site