Deployment: WSGI, Static & Settings
Deployment is the process of configuring and running your Django project on a production server safely, with debugging off, secrets in the environment, static files collected, and a real WSGI server handling requests.
Learn Deployment: WSGI, Static & Settings in our free Django course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free Django course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll harden settings with DEBUG=False , ALLOWED_HOSTS , and an environment-driven SECRET_KEY , collect static files with collectstatic and WhiteNoise, serve the app through gunicorn and WSGI, parse a database URL, and run the deploy checklist.
What You'll Learn in This Lesson
1 Production Settings from the Environment
The most important production change is turning DEBUG off and listing your real domains in ALLOWED_HOSTS . Secrets like SECRET_KEY must come from the environment, never from a file in your repository. You read them with os.environ at startup.
2 Static Files, the Database URL, and gunicorn
In production the dev server doesn't serve static files, so you run collectstatic to gather them into STATIC_ROOT and let WhiteNoise serve them. Your database connection arrives as a single DATABASE_URL , and you run the app through gunicorn , which loads your project's WSGI callable.
3 The Deploy Checklist (check --deploy)
Django ships a built-in safety audit: python manage.py check --deploy . It inspects your settings and warns about anything unsafe for production — DEBUG left on, an empty ALLOWED_HOSTS , a weak SECRET_KEY , or missing SSL settings. Run it before every release.
🎯 Your Turn: Read DEBUG from the Environment
Environment variables are strings, so "False" is truthy. Fill in the blank so DEBUG becomes the boolean False only when the variable is exactly "True" .
Common Errors (And How to Fix Them)
❌ DisallowedHost / Bad Request (400) after deploy
With DEBUG=False , Django rejects any host not listed in ALLOWED_HOSTS .
✅ Fix: add your domain(s) to ALLOWED_HOSTS , e.g. ["example.com", "www.example.com"] .
You never collected static files, or nothing is serving them.
✅ Fix: run collectstatic and add WhiteNoise middleware (or a CDN) to serve STATIC_ROOT .
A hard-coded SECRET_KEY in settings.py ends up in version control.
✅ Fix: read it from os.environ and store the value in your platform's secret manager.
📋 Quick Reference
Setting / Command
Why It Matters
DEBUG = False
Hide tracebacks; enforce ALLOWED_HOSTS
ALLOWED_HOSTS
Domains the site is allowed to serve
collectstatic
Gather static files into STATIC_ROOT
gunicorn project.wsgi
Run the app as a WSGI server
check --deploy
Audit settings for production safety
🏆 Mini Challenge: A Deploy-Readiness Gate
Build a gate that loads settings from the environment, runs the production checklist, and only "deploys" when there are zero issues — otherwise it reports the blocking problems.
❓ Frequently Asked Questions
Lesson complete — you can ship to production!
You can harden settings with DEBUG=False , ALLOWED_HOSTS , and an env-driven SECRET_KEY , collect static files with collectstatic and WhiteNoise, run the app through gunicorn and WSGI, parse a database URL, and audit with check --deploy .
🚀 Up next: Checkpoint — Build a Django REST API — put pagination, auth, and caching together in one project.
Practice quiz
What must DEBUG be set to in production?
- True
- False
- 1
- 'on'
Answer: False. DEBUG=False hides tracebacks, enforces ALLOWED_HOSTS, and serves a generic 500 page.
Why is DEBUG=True dangerous in production?
- It makes the site slower only
- It disables the database
- It exposes code, settings, and environment in error pages
- It blocks all requests
Answer: It exposes code, settings, and environment in error pages. The debug error page leaks code, settings, and env vars to anyone who triggers an error.
What does collectstatic do?
- Compiles your Python code
- Gathers static files into STATIC_ROOT
- Minifies JavaScript only
- Starts the web server
Answer: Gathers static files into STATIC_ROOT. collectstatic gathers static files from all apps into the single STATIC_ROOT directory.
What is WSGI?
- A database driver
- A template engine
- A caching backend
- The standard interface between a Python web app and a server
Answer: The standard interface between a Python web app and a server. WSGI is the standard interface; project/wsgi.py exposes the 'application' callable.
Which command starts a production WSGI server for project/wsgi.py?
- gunicorn project.wsgi:application
- python manage.py runserver
- django-admin serve
- wsgi start project
Answer: gunicorn project.wsgi:application. gunicorn imports the WSGI application callable and serves it with worker processes.
What does WhiteNoise let you do?
- Encrypt the database
- Serve collected static files efficiently from the app server
- Run background tasks
- Manage migrations
Answer: Serve collected static files efficiently from the app server. WhiteNoise serves the collected static files with caching headers, the simplest production option without a CDN.
Why must ALLOWED_HOSTS be set when DEBUG=False?
- It speeds up rendering
- It configures static files
- Django rejects any host not listed, raising DisallowedHost
- It enables the admin
Answer: Django rejects any host not listed, raising DisallowedHost. With DEBUG off Django enforces ALLOWED_HOSTS and returns 400 for unlisted hosts.
How should SECRET_KEY be supplied in production?
- Hard-coded in settings.py
- Committed to the repository
- Generated fresh on every request
- Read from an environment variable, never committed
Answer: Read from an environment variable, never committed. Read SECRET_KEY from os.environ and keep it out of source control.
Which command audits settings for production safety?
- python manage.py check --deploy
- python manage.py audit
- python manage.py secure
- python manage.py validate
Answer: python manage.py check --deploy. check --deploy warns about DEBUG, empty ALLOWED_HOSTS, weak SECRET_KEY, and missing SSL settings.
Why compare an env var explicitly with == 'True' for DEBUG?
- It is faster than bool()
- Env vars are strings, so even 'False' is truthy
- Django requires the string form
- It avoids importing os
Answer: Env vars are strings, so even 'False' is truthy. Environment values arrive as strings; any non-empty string is truthy, so compare to 'True'.