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 osztromok_site.wsgi:application --bind 127.0.0.1:8000 --workers 3

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

ALLOWED_HOSTS = ['osztromok.com', 'www.osztromok.com']

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

import os SECRET_KEY = os.environ['DJANGO_SECRET_KEY']

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.

Rotating SECRET_KEY logs out every admin, immediately
Because 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

python manage.py collectstatic --noinput # gathers every app's own static/ folder — including Chapter 5's content/static/content/... # and Chapter 7's animcjk/ assets — into one STATIC_ROOT, ready for nginx to serve directly

nginx as Reverse Proxy

server { listen 80; server_name osztromok.com; location /static/ { alias /path/to/staticfiles/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; } }

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

A genuine, honest difference from Next.js
Vercel is built by the Next.js team, specifically for Next.js — near-zero-config deployment is possible precisely because the platform and the framework share an owner. Django has no equivalent: no company-run platform built specifically around Django's own conventions the same way. Django deployment is always closer to the traditional self-hosted pattern shown here (or a general-purpose PaaS like Render or Railway, which supports Django well but wasn't built for it specifically) — which, honestly, mirrors this project's own already-live PHP/Apache reality more closely than Next.js's own Vercel path ever could.
Next.jsDjango
Zero-config platformVercel — built by the same team, for this exact frameworkNone — no Django-specific equivalent exists
Self-hosted pathPM2 + nginx, covered as the alternative to Vercelgunicorn + nginx — the only real path shown here

Hands-On Exercises

Exercise 1

Explain why manage.py runserver is explicitly not meant for production use, and what gunicorn provides that it doesn't.

📄 View solution
Exercise 2

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.

📄 View solution
Exercise 3

Explain why rotating SECRET_KEY immediately logs out every currently-logged-in admin, including the account imported back in Chapter 9.

📄 View solution

Chapter 11 Quick Reference

  • gunicorn — the real production WSGI server; runserver is explicitly dev-only
  • DEBUG=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 once DEBUG=False; rejects unrecognized Host headers
  • SECRET_KEY — signs sessions and every CSRF token; belongs in an environment variable, never hardcoded
  • Rotating SECRET_KEY logs everyone out — a direct, expected consequence, not a bug
  • collectstatic — gathers every app's static files into STATIC_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