Deploying, HTTPS and the launch checklist
Everything so far has prepared for this moment: getting Nidaan onto the public internet, served over HTTPS,
running reliably. This lesson ties the deployment module together — the real server architecture (not
runserver), how HTTPS actually happens, the choice of where to host, and a launch checklist you run before
declaring a site live. It is the last mile, and getting it right is what turns a project into a product.
The production serving architecture
The development runserver is one process doing everything, insecurely and slowly. Production is a small
stack, each part with one job:
Internet → [ Web server / proxy ] → [ Application server ] → [ Django ]
(Nginx, or the (Gunicorn / Uvicorn)
platform's router)
- The application server (Gunicorn for WSGI/sync, Uvicorn for ASGI/async) runs your Django code
in several worker processes, so many requests are handled concurrently. This replaces
runserver— it is built for production load. - The web server / reverse proxy (Nginx, or a platform's built-in router) sits in front: it terminates HTTPS, serves static files fast, buffers requests, and forwards the rest to the application server. On a platform-as-a-service this layer is provided for you.
The rule stated throughout, now final: runserver never touches production. It is single-threaded,
unoptimised, and not built to face the internet. Production runs Gunicorn/Uvicorn behind a proxy.
HTTPS is mandatory, and now it is easy
Every production site must be served over HTTPS — it encrypts traffic (so sessions, passwords and, for a clinic, patient data cannot be read in transit) and is required for the secure-cookie and HSTS settings from the settings-for-production lesson. Obtaining a certificate used to be a chore; today it is free and automated:
- Let's Encrypt issues free certificates, and tools like Certbot obtain and auto-renew them on a server you manage.
- Most platforms-as-a-service provision and renew HTTPS for you automatically when you add a domain — you do nothing but point your DNS at them.
Once HTTPS is in place, turn on SECURE_SSL_REDIRECT, the secure cookies, and HSTS (settings-for-production)
so the whole site is encrypted and browsers refuse the insecure version. HTTPS is not optional for a real
application, and there is no longer any excuse — it is free and, on a good platform, automatic.
Where to host: the honest options
Match the host to your team's size and appetite for operations:
- Platform-as-a-service (Railway, Render, Fly.io, Heroku-style, or similar) — you push code (or a container) and the platform runs it, provisions HTTPS, and offers a managed PostgreSQL. Least operational work, fastest to launch — the sensible default for a small team, so you spend time on the app, not on server administration. Cost scales with usage.
- A VPS you manage (a plain Linux server) — you install and configure Nginx, Gunicorn, PostgreSQL, certificates and monitoring yourself. Most control and often cheapest at the margin, but real ongoing operational work (security updates, backups, uptime are now your job).
- Container platforms / bigger cloud — for scale beyond a single small app; more power, more complexity.
The cost-conscious, effort-conscious default for a clinic app is a platform-as-a-service with a small managed PostgreSQL — cheap at low traffic, HTTPS handled, backups handled, and no server to babysit. Move to more control only when you have a concrete reason.
The deploy process itself
However you host, a deploy runs a repeatable sequence — ideally automated (a script or CI pipeline), never hand-typed each time:
- Build — install the exact dependencies (
requirements.txt) or build the container image. - Migrate —
python manage.py migrateto apply schema changes to the production database. (Plan destructive migrations carefully; they can lose data.) - Collect static —
collectstatic --noinputso the current static files are served. - Check —
python manage.py check --deploymust be clean (the settings-for-production checklist). - Restart — start/restart the application server (Gunicorn) with the new code.
Automating this makes deploys boring and repeatable — which is exactly what you want. A deploy you run by remembering five commands is a deploy you eventually get wrong.
The launch checklist
Before declaring Nidaan live, run through this — it is the whole module, condensed:
-
DEBUG = False, realALLOWED_HOSTS,SECRET_KEYfrom the environment. -
python manage.py check --deployis clean (all warnings cleared). - HTTPS works, with
SECURE_SSL_REDIRECT, secure cookies and HSTS on. - PostgreSQL in use (not SQLite), with tested backups configured.
- Static files collected and served (WhiteNoise or a server/CDN); media on durable storage, with sensitive files access-controlled.
- Running under Gunicorn/Uvicorn behind a proxy — not
runserver. - Logging and error tracking (e.g. Sentry) configured, so failures are visible.
- Migrations applied; the deploy process is automated and repeatable.
- Verify the live site actually works — load real pages over HTTPS, submit a form, check the admin — rather than assuming the deploy succeeded.
That last point is the habit that catches the surprises: a deploy "succeeding" is not the same as the site working — fetch the live pages and confirm, do not assume. Work down the checklist and going live is a verified, unremarkable step, which is exactly what a good deployment should be.
Check your work
The production architecture. Internet → web server/proxy (Nginx or platform router: HTTPS, static,
buffering) → application server (Gunicorn/Uvicorn, many workers) → Django. runserver never in production.
HTTPS. Mandatory (encrypts traffic, required for secure cookies/HSTS); free and automated via Let's
Encrypt/Certbot or provisioned automatically by a platform — then enable SECURE_SSL_REDIRECT, secure
cookies, HSTS.
Where to host. Platform-as-a-service (least ops, sensible default for a small team) versus a self-managed VPS (most control/ops) versus bigger cloud (scale). Default: PaaS + small managed PostgreSQL.
The deploy process. Build → migrate → collectstatic → check --deploy → restart — automated and
repeatable, not hand-typed.
The launch checklist. DEBUG=False/hosts/secret; check --deploy clean; HTTPS + secure settings;
Postgres + tested backups; static served + media durable/access-controlled; Gunicorn behind a proxy; logging/
error tracking; migrations applied; and verify the live site works rather than assuming.
Practice
- Run a Django app under Gunicorn locally (
gunicorn config.wsgi:application) instead ofrunserver, and confirm it serves. - Deploy Nidaan to a platform-as-a-service (or describe the exact steps): push, add a domain, confirm the platform provisions HTTPS.
- Enable
SECURE_SSL_REDIRECTand the secure cookies; confirmhttp://redirects tohttps://andcheck --deployis clean. - Write a deploy script that runs build → migrate → collectstatic → check → restart in order.
- Configure a database backup and restore it to prove it works.
- Run the full launch checklist against a deployed Nidaan, ending by fetching live pages over HTTPS to verify — not assume — it works.
Official documentation
- Django — Deployment overview — WSGI/ASGI servers and the production setup.
- Django — How to deploy with WSGI (Gunicorn) — Running under Gunicorn.
- Let's Encrypt — Free, automated HTTPS certificates.
Next: the capstone brief — a clinic and diagnostic-lab manager.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship