Why is Django's runserver command not meant for production, and what serves a Django project there instead?
answer
- built for the edit-reload loop
- never audited or load-tested
- a separate application server
- module path plus :application
basics
~20 srunserver is a development server: it auto-reloads, runs in one process and was never security-audited or performance-tested. Production runs the project's wsgi.py or asgi.py application object under a real WSGI or ASGI server, usually behind a reverse proxy.
solid answer
~40 s`manage.py runserver` exists for the edit-reload loop: it restarts on code changes, runs system checks, and with `django.contrib.staticfiles` serves static files while `DEBUG` is true. Django's docs state it has not been through security audits or performance tests, and since Django 5.2 it prints a warning saying so at start-up. It is a single process that starts a thread per request, with nothing to manage or recycle workers. In production you point a WSGI server (Django's docs cover Gunicorn, uWSGI, mod_wsgi and Granian) at `myproject.wsgi:application`, or an ASGI server (Daphne, Hypercorn, Uvicorn, Granian) at `myproject.asgi:application`, and put a reverse proxy in front for TLS, slow clients and static files.
code
bash · 8 lines# development
python manage.py runserver
# production: a WSGI server loads the application object from wsgi.py
gunicorn myproject.wsgi:application
# or an ASGI server loads it from asgi.py
uvicorn myproject.asgi:applicationgo deeper
Know that runserver is only for development and that production runs a WSGI or ASGI server pointed at the project's application object.
Explain what runserver does for you (reloader, checks, static files under DEBUG), which setting it reads, and how a production server is given myproject.wsgi:application.
Describe the production stack of reverse proxy, application server and Django, which job each layer owns, and the settings that must change alongside the server.
Choose the serving stack for a team: WSGI or ASGI server, proxy responsibilities and a standard configuration every Django service shares.
## What runserver is for `python manage.py runserver` (or `django-admin runserver`) is Django's **development server**. It is built around a developer's edit-and-refresh loop: - the **auto-reloader** restarts the server when Python code changes; - the **system check framework** runs at start-up and after each reload; - it is **multithreaded by default**, creating a new thread for each request (`--nothreading` turns that off); - with `django.contrib.staticfiles` installed, it serves static files — but only while `DEBUG = True`, or with the `--insecure` flag; - it binds to `127.0.0.1:8000` by default, reachable only from the same machine. It loads the WSGI application named by the **`WSGI_APPLICATION`** setting, which `startproject` sets to `<project>.wsgi.application`. It never uses `asgi.py`. ## Why it is not a production server The docs are blunt: *do not use this server in a production setting*. Since **Django 5.2** it also prints a warning at start-up saying it is a development server (suppressed only by setting the `DJANGO_RUNSERVER_HIDE_WARNING` environment variable to `"true"`). The reasons: 1. **No security audit and no performance testing.** Making it production-grade is explicitly outside Django's scope. 2. **One process.** There is no pool of worker processes to spread load across CPU cores, replace a crashed or bloated worker, or restart gracefully during a deploy. 3. **Static files the slow way.** The staticfiles handler that `runserver` uses is described in the docs as grossly inefficient and probably insecure, and it switches off when `DEBUG` is false. 4. **Development features cost resources.** The reloader watches the code tree, and a thread per request with no upper limit is not a capacity model. ## What production looks like | Layer | Job | Django's part | |---|---|---| | Reverse proxy | TLS, buffering slow clients, serving static and media files, routing | none; Django only needs to trust what it forwards | | Application server | manages worker processes or event loops, speaks WSGI or ASGI | loads `application` from `wsgi.py` or `asgi.py` | | Django application object | turns each request into a response through middleware, URLconf and views | built once per process by `get_wsgi_application()` or `get_asgi_application()` | The application server is given a dotted path such as `myproject.wsgi:application` or `myproject.asgi:application`. Django's deployment docs include getting-started guides for Gunicorn, uWSGI, mod_wsgi and Granian on the WSGI side, and Daphne, Hypercorn, Uvicorn and Granian on the ASGI side; which one you pick is an operations choice, not a Django one. ## Settings that change with the server Moving off `runserver` goes together with production settings: `DEBUG = False`, a real `ALLOWED_HOSTS`, HTTPS-related settings, and static files collected and served by something other than the development handler. Those have their own checklist (`manage.py check --deploy`), but a candidate should at least mention that switching servers without switching settings is only half the job. ## What interviewers listen for - the plain statement that `runserver` is for development only, with a reason beyond "it's slow"; - that production uses a separate WSGI or ASGI server pointed at the project's `application` object; - that a reverse proxy normally sits in front; - bonus: static files stop being served by `runserver` once `DEBUG` is off, which is why pages lose their CSS the first time people try it.
- A page loses all its CSS as soon as DEBUG is set to False under runserver. Why?The staticfiles version of `runserver` serves static files only while `DEBUG` is true, or when started with `--insecure`. With `DEBUG = False` it stops, so static URLs return 404. In production static files are collected with `collectstatic` and served by the proxy or a dedicated static-files layer, not by the development handler.
- Which file does runserver load, and which one does an ASGI server load?`runserver` loads the WSGI callable named by the `WSGI_APPLICATION` setting, by default `application` in `<project>/wsgi.py`, and never touches `asgi.py`. An ASGI server is given `<project>.asgi:application` directly. Both files build the same Django project; they differ in the protocol the server speaks.
saying these in an interview costs you the question
- runserver is fine in production if you pass --noreload
- Binding runserver to 0.0.0.0 makes it production-ready
- runserver keeps serving static files after DEBUG is set to False
- runserver uses asgi.py when the project has async views
- The only problem with runserver is that it is single-threaded