Most Django production problems live in the serving layer, not in Django itself: workers timing out on long requests, memory growing until the server stalls, static files mysteriously 404ing after a deploy, a certificate expiring on a Sunday. Our Nginx and Gunicorn deployment treats that layer as the subject — whether we're building it fresh or rescuing one that's misbehaving.
Think of this as the specialist engagement for the reverse-proxy-plus-application-server layer: the configuration craft, the worker tuning, the hardening, and the diagnosis when production is slow or flaky and the application code looks fine.
The rescue cases we're called in for
- Intermittent 502 errors under load — usually worker timeouts set too aggressively, or too few workers for the traffic. The application is fine; the serving layer is dropping requests it could have handled.
- The server slows over days, fine after a restart — classic worker memory growth with no recycling configured. Restarting treats the symptom; configuring worker recycling treats the cause.
- Static files break after every deploy — the static-file collection step isn't in the deploy path, or the Nginx location mapping doesn't match where files actually land. Predictable, and permanently fixable.
- The site went down when the certificate expired — no automatic renewal, no expiry monitoring. An entirely preventable outage.
- Admin and login pages getting hammered — no rate limiting at the proxy, so every bot on the internet gets a full Django request cycle. Nginx should absorb this before it reaches your workers.
- One config copied across all sites — the same worker count and timeouts on a tiny internal tool and the customer-facing app. Each workload deserves its own tuning.
If any of these sound familiar, the read-only audit is the right starting point — it finds the actual causes before anything is changed.
The serving-layer checklist
Whether fresh or rescue, the layer gets built to the same standard. Nginx handles the internet-facing work: TLS termination, static and media file serving, buffering slow clients so application workers aren't held hostage by bad connections, rate limiting on sensitive endpoints, and security headers. Gunicorn runs the Python: the worker model matched to the workload — synchronous workers for typical request-response Django, async workers where long-lived connections justify them — with counts derived from the server's resources and the application's memory profile, timeouts set for the longest legitimate work, and worker recycling so memory growth can't accumulate silently. systemd supervises it all: automatic restarts, boot-time startup, and logs captured with rotation so a full disk never takes down the server.
Tuning is measurement, not guesswork
We don't tune from rules of thumb alone. The engagement starts from your reality: access and error logs showing which requests are slow, memory profiles showing what workers actually consume, traffic patterns showing when the load arrives. Worker counts, timeout values, and buffer sizes come out of that evidence — and the reasoning is documented alongside the settings, so the next person to touch the configuration understands why each value is what it is. Anyone offering to "optimise" your serving layer without reading your logs first is guessing.
When the serving layer isn't the problem
Candour matters here: sometimes the 502s are a database query that takes thirty seconds, or application code holding a lock. The audit distinguishes serving-layer issues from application and database issues before we propose work — if the fix belongs in your code or your queries, we'll tell you that instead of rebuilding a proxy that was never the problem. Similarly, if your traffic has genuinely outgrown a single server, the answer is orchestration — see our Kubernetes deployment services — not a better-tuned single box.
What you need for this engagement
- A Django application already running or ready to run — this engagement is the serving layer, not the application build.
- Server access for us to work with (read-only for the audit phase; full during implementation).
- For rescues: access to the current Nginx and Gunicorn configurations and recent logs. The logs usually contain the diagnosis; we just need to read them.
Frequently asked questions
Our Django site is slow. Is the serving layer the problem?
Sometimes — and the audit tells us which sometimes. Worker starvation, aggressive timeouts, and missing static-file handling are serving-layer causes with distinctive signatures in the logs. Slow database queries and application-level bottlenecks have different signatures. We diagnose from evidence before proposing work, so you don't pay for a proxy rebuild when the problem was a missing database index.
Synchronous vs async Gunicorn workers — which do we need?
Synchronous workers suit typical Django work: requests come in, get processed, go out. Async workers suit workloads with many concurrent long-lived connections — held-open requests, streaming responses. Most Django applications are firmly in the synchronous camp, and async workers add complexity without benefit there. We choose from your workload's actual behaviour, not from what's fashionable.
Can one server run several Django applications behind Nginx?
Yes, and it's a common setup. Nginx routes each domain to its own backend via separate server blocks; each application runs as its own supervised Gunicorn service with independent workers, logs, timeouts, and resource boundaries. Done this way, one application's traffic spike or bad deploy doesn't affect the others — the isolation is structural, not hoped for.
Do you only work with Django here?
This engagement is Django-focused because that's where the Nginx-plus-WSGI pattern is most standard. The Nginx craft — proxying, TLS, rate limiting, static handling — applies to any Python WSGI application, and much of it to other backends entirely. If your stack is adjacent, ask; we'll tell you plainly whether the engagement fits.
What does a rescue engagement look like?
Read-only audit first: we review configurations and logs and produce findings ranked by impact — what's actually causing the symptoms, what's a risk waiting to happen, what's merely untidy. You approve the fix plan before anything changes. Then implementation, verification against your real traffic, and documented configurations. No changes without your sign-off, and no "while we were in there" surprises.
Fix the layer, or build it right
Whether your serving layer needs rescuing or building from scratch, start with a conversation about the symptoms — we'll tell you which one it is. Contact us. For the full-stack Django deployment story, see Django Nginx deployment in Uttarakhand and Django deployment in Dehradun; for ongoing protection, website security maintenance and server monitoring setup.