Run Gunicorn on test/prod instead of Django's dev runserver (#1034)#1385
Merged
Conversation
Django's `runserver` is a development server the docs explicitly warn
against in production ("has not gone through security audits or
performance tests"). We had been running it on both test and prod since
2017. Swap it for Gunicorn, the recommended WSGI server.
The change is entirely inside the container: Apache still reverse-proxies
dynamic requests to 127.0.0.1:8571 and serves /static/ and /media/
directly, so this ships via the normal push-to-deploy path with no
Apache/UW CSE IT changes.
- requirements.txt: add gunicorn==23.0.0
- docker-entrypoint.sh: start Gunicorn when DJANGO_ENV is TEST or PROD;
keep `runserver` for local dev (DJANGO_ENV=DEBUG) so auto-reload, the
debug toolbar, and DEBUG=True static serving still work. Workers (3)
and timeout (120s) default conservatively for the shared host and are
overridable via GUNICORN_WORKERS / GUNICORN_TIMEOUT.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1034.
Swaps Django's dev
runserverfor Gunicorn on test/prod. The Django docs explicitly warn againstrunserverin production ("has not gone through security audits or performance tests"), yet we've run it on both test and prod since 2017.Key point: no UW CSE IT involvement needed
The Django process runs inside our Docker container, behind UW CSE's Apache reverse proxy (
127.0.0.1:8571→ container:8000). Swapping the in-container server leaves the port mapping and everything Apache sees identical, so this ships via the normal push-to-deploy path — push tomaster→ test, tag → prod. No Apache edits, no IT ticket.One thing worth confirming with @mechanicjay (verification only): that Apache serves
/static/and/media/from the filesystem rather than proxying them to the container. Almost certainly yes — prod runsDEBUG=False, andrunserverrefuses to serve static whenDEBUG=False, yet our CSS/JS work in prod.Changes
requirements.txt: addgunicorn==23.0.0.docker-entrypoint.sh: start Gunicorn whenDJANGO_ENVisTEST/PROD; keeprunserverfor local dev (DJANGO_ENV=DEBUG) so auto-reload, the debug toolbar, andDEBUG=Truestatic serving still work. Usesexecso the server becomes PID 1 and receives Docker's stop signals.Tuning (overridable via env vars)
GUNICORN_WORKERSdefaults to 3 — the(2*cores)+1rule of thumb would be ~49 on the 24-core host, but that box is shared with all Project Sidewalk instances (Makeability Lab website slow to load #959), so we stay conservative.GUNICORN_TIMEOUTdefaults to 120s — Gunicorn's default 30s could kill slow ImageMagick/PDF thumbnail generation.Why this is not a latency fix
The #959 slowness was N+1 queries (since fixed via the ORM). Gunicorn won't speed up a single render; it makes serving production-grade (security, managed workers, crash-restart, request timeouts).
Test plan
master→ auto-deploys tomakeabilitylab-test.-test: browse pages; save an admin record with an image upload (exercises the 120s timeout path); confirm/static/and/media/still load.🤖 Generated with Claude Code