The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
Deploy Django’s ASGI application behind Nginx, run it under Gunicorn with Uvicorn workers, and connect it to PostgreSQL through environment-backed settings. The production path is: prepare a secured Linux host, install Django and the ASGI/PostgreSQL dependencies, configure and migrate PostgreSQL, collect static files, supervise the ASGI process, proxy it through Nginx, enable HTTPS, and pass Django’s deployment checks before switching traffic.
What the production architecture looks like
Django creates an ASGI callable in <project>/asgi.py. Uvicorn loads that callable with the module:application syntax, such as myproject.asgi:application. ASGI is Django’s interface for asynchronous-capable request handling and WebSockets, although individual Django views and dependencies may still be synchronous.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesIn a self-hosted deployment, the usual request path is:
- Client: connects over HTTPS.
- Nginx: terminates TLS, serves files from
STATIC_ROOT, and proxies dynamic requests. - Gunicorn: supervises worker processes and restarts them when configured by the service manager.
- Uvicorn worker: runs Django’s ASGI callable.
- PostgreSQL: stores application data through Django’s PostgreSQL backend and psycopg.
Use a Unix-domain socket between Nginx and the application process when both run on the same host. It avoids exposing the application server on a public TCP port and is the pattern recommended in Uvicorn’s deployment guidance. A managed platform can provide some of this supervision and networking for you, but the Django settings, database migrations, static files and security checks remain application responsibilities.
#1 Best Overall
Prerequisites and decisions before installation
Host and project requirements
- A production Linux host or managed platform with a service supervisor or container supervisor.
- A deployable Django project whose directory containing
manage.pyis known. - A Python environment in which the project and its dependencies can be imported.
- A PostgreSQL server reachable from the application host.
- A domain name and an HTTPS termination point, normally Nginx or an upstream load balancer.
Run deployment commands from the directory that contains manage.py, or otherwise make the project package importable through PYTHONPATH. Set DJANGO_SETTINGS_MODULE in the service environment if your project does not set it itself.
Keep secrets out of source control
Provide SECRET_KEY, database credentials and environment-specific host settings through the host’s secret store or environment. Do not commit them to the repository. The production settings must also define the real domain in ALLOWED_HOSTS; do not leave DEBUG enabled on an Internet-facing deployment.
Choose the process model
| Option | Best use | Operational characteristics |
|---|---|---|
| Direct Uvicorn | Development, diagnostics or a platform that already supervises the process | Simple command line; process restart, logging and resource limits must come from the surrounding platform. |
Gunicorn with uvicorn_worker.UvicornWorker |
Self-hosted production | Gunicorn manages worker processes while each worker runs the ASGI application; pair it with systemd or a container supervisor. |
| Managed platform | Teams that want hosted process and network operations | The platform may handle TLS, restarts and health checks, but you still configure Django, PostgreSQL access, migrations and static assets. |
Compare options by restart behavior, worker and database-connection limits, Unix-socket versus TCP networking, TLS termination, static-file handling, WebSocket support, observability, rollback strategy and the operational work your team will own.
Install Django, Uvicorn, Gunicorn and PostgreSQL support
Create or activate the same virtual environment that your service will use. Install the PostgreSQL driver, Uvicorn, the Uvicorn worker package and Gunicorn. Django’s documented installation example is:
python -m pip install uvicorn uvicorn-worker gunicorn
Install Django and the project’s other pinned dependencies as part of your normal dependency installation. The application server command uses the separate uvicorn-worker package, whose import path is uvicorn_worker.UvicornWorker.
Check the installed versions
Current Django documentation supports PostgreSQL 14 or newer and recommends psycopg 3.1.12 or newer. Verify those requirements against the Django version actually installed, because support can change between Django releases. Confirm the import path before starting the service:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #2
python -c "import django, psycopg, uvicorn, gunicorn; print(django.get_version())"
python manage.py check
If your project uses psycopg 3, install its pooling extra when you intend to use Django’s pool option, for example through psycopg[pool] or the separate psycopg-pool package. The pool option is ignored by psycopg2.
Configure PostgreSQL through Django settings
Use Django’s PostgreSQL backend and read every environment-specific value from the process environment. This pattern keeps credentials outside the codebase and allows the same build to run in different environments.
import os
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["POSTGRES_DB"],
"USER": os.environ["POSTGRES_USER"],
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
"HOST": os.environ.get("POSTGRES_HOST", "127.0.0.1"),
"PORT": os.environ.get("POSTGRES_PORT", "5432"),
}
}
Database values that must be correct
- NAME: the application database.
- USER and PASSWORD: a dedicated application role, not a superuser.
- HOST and PORT: the private address and PostgreSQL port reachable from the application host.
- Encoding: create the database with UTF-8 encoding.
- Time zone: align PostgreSQL and Django’s time-zone settings with the application’s intended behavior.
Confirm network access and role permissions before cutover. Grant the application role only the permissions it needs. Plan automated backups and perform restore tests; a backup that has never been restored is not a verified recovery plan.
Connection behavior under ASGI
For ASGI deployments, disable persistent Django connections by leaving CONN_MAX_AGE at its non-persistent setting rather than enabling long-lived connections indiscriminately. Use Django’s or psycopg’s pooling deliberately when concurrency warrants it:
# Example only when psycopg[pool] or psycopg-pool is installed
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["POSTGRES_DB"],
"USER": os.environ["POSTGRES_USER"],
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
"HOST": os.environ.get("POSTGRES_HOST", "127.0.0.1"),
"PORT": os.environ.get("POSTGRES_PORT", "5432"),
"OPTIONS": {"pool": True},
}
}
Size the pool from expected in-flight query concurrency and the database server’s connection budget. Every worker and pool slot consumes database capacity, so increasing web concurrency without accounting for PostgreSQL connections can exhaust the server.
Django documents READ COMMITTED as PostgreSQL’s default transaction isolation level. Change isolation only when the application’s transaction requirements justify the additional complexity.
Prepare the Django application
Run migrations
After the database role can connect, apply schema migrations:
python manage.py migrate
Run this against the intended production database, not a local development database accidentally selected by inherited environment variables. If a migration fails, fix the underlying schema or permission problem and rerun it after confirming the migration’s partial state is safe.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Collect static files
Set STATIC_ROOT to a directory that Nginx can read, then collect the files without prompting:
python manage.py collectstatic --noinput
Do not ask Uvicorn or Django to serve static files in production. Nginx should serve the collected directory directly, while dynamic requests continue to the ASGI application.
Verify the ASGI callable
The generated file is normally myproject/asgi.py and exposes an object named application. Test the import from the project directory:
python -c "import myproject.asgi; print(myproject.asgi.application)"
Replace myproject with the package that actually contains asgi.py. An import error here will also prevent Gunicorn from starting.
Rank #4
Start the ASGI application
Direct Uvicorn command
For development or a platform that already handles supervision, start the callable directly:
python -m uvicorn myproject.asgi:application
Bind it to a private interface or socket when Nginx is in front of it. The built-in Django runserver command is a development server and is not suitable for production.
Gunicorn with Uvicorn workers
For a self-hosted production process, use Gunicorn with the Uvicorn worker class:
python -m gunicorn myproject.asgi:application -k uvicorn_worker.UvicornWorker
Add an explicit bind address or Unix socket in the service configuration. A socket path such as /run/myproject/uvicorn.sock must exist in a directory whose ownership and permissions allow both Gunicorn and Nginx to use it. Let systemd, a container supervisor or an equivalent process manager start the command, restart it on failure and capture logs. Do not rely on an SSH session remaining open.
Free tools Windows power users keep installed
One-click scans. No signup required.
Service-manager responsibilities
- Set the working directory to the directory containing
manage.py. - Activate or reference the production virtual environment.
- Set
DJANGO_SETTINGS_MODULEand all required secrets. - Create and protect the runtime directory used for the Unix socket.
- Restart the service after code or dependency changes.
- Send standard output and error to a log destination that operators can inspect.
Configure Nginx as the reverse proxy
The following configuration serves collected static files and forwards dynamic requests through a Unix socket:
upstream django_asgi {
server unix:/run/myproject/uvicorn.sock;
}
server {
listen 80;
server_name example.com;
location /static/ {
alias /srv/myproject/static/;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://django_asgi;
}
}
Change the domain, socket and static directory to your actual paths. The directory in an alias must contain the files produced by collectstatic, and Nginx needs read and traverse permissions on every parent directory.
Best Value
Forwarded headers and proxy trust
Host, X-Forwarded-For and X-Forwarded-Proto let Django reconstruct the original request host, client chain and HTTP scheme. Configure Django’s proxy-related trust settings to match the actual number and identity of proxies. Never accept forwarded headers from arbitrary clients; a directly reachable application server can otherwise be made to trust spoofed scheme or client information.
WebSocket support
Only add WebSocket upgrade handling when the application uses WebSockets. Use a controlled connection map rather than blindly forwarding upgrade values:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name example.com;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://django_asgi;
}
}
Keep WebSocket timeout and buffering choices consistent with the application’s expected connection lifetime. Ordinary HTTP deployments do not need these headers.
Enable HTTPS and release protections
Terminate TLS at Nginx or an upstream load balancer, redirect HTTP requests to HTTPS, and ensure the proxy passes X-Forwarded-Proto correctly. Use secure cookie settings and configure CSRF trusted origins for the exact HTTPS origins your users will access.
Before sending traffic to the new deployment, run Django’s deployment checks with production settings:
python manage.py check --deploy
Review every warning, especially:
ALLOWED_HOSTScontains only the intended hostnames.DEBUGis disabled.- Session and CSRF cookies are secure and appropriate for HTTPS.
- CSRF origin settings include the correct scheme and host.
- HSTS is enabled only after HTTPS is working consistently for every required hostname.
- Error reporting does not disclose secrets or sensitive request data.
Expose a protected health endpoint and have the supervisor or load balancer check it. A health check should verify that the process is responsive; a deeper readiness check can also verify required database connectivity without revealing credentials or internal details.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Complete deployment sequence
- Prepare the host: create the runtime user and directories, install Python, Nginx and PostgreSQL client components, and place the project in the directory containing
manage.py. - Set secrets and environment: provide
DJANGO_SETTINGS_MODULE,SECRET_KEY, database values and allowed hosts through the service environment. - Install dependencies: install Django, psycopg, Uvicorn,
uvicorn-worker, Gunicorn and the project’s pinned packages. - Validate imports: run
python manage.py checkand importmyproject.asgi:application. - Configure PostgreSQL: create the UTF-8 database and least-privilege application role, then verify network access and permissions.
- Apply schema: run
python manage.py migrate. - Collect assets: run
python manage.py collectstatic --noinputinto the directory Nginx will serve. - Create the service: run Gunicorn with
uvicorn_worker.UvicornWorker, bind it to the chosen Unix socket, and enable restart and logging behavior. - Configure Nginx: serve
STATIC_ROOT, proxy dynamic requests, forward the required headers and add WebSocket handling only if needed. - Enable HTTPS: configure certificates, redirect HTTP, and verify secure cookie and CSRF settings.
- Run release gates: execute
python manage.py check --deploy, test the health endpoint, load a dynamic page, submit a form protected by CSRF and retrieve a static asset. - Cut over and monitor: switch DNS or the load balancer, watch application and Nginx logs, and keep a rollback plan for the previous application version and database migration.
Troubleshoot common failures
Gunicorn exits immediately
- Confirm the working directory contains
manage.pyand that the project package is importable. - Check that the module path matches the package containing
asgi.py. - Run the import test directly and inspect the service’s captured stderr.
- Verify that every required environment variable is present in the service environment, not only in your interactive shell.
Nginx returns 502 Bad Gateway
- Check that Gunicorn is running and the socket file exists.
- Compare the socket path in Nginx with the path in the service command.
- Fix ownership and permissions on the socket and its parent directories.
- Read both Nginx’s error log and the Gunicorn service log; a Python import or database startup error usually appears in the latter.
Static files return 404
- Run
collectstatic --noinputwith the production settings. - Confirm
STATIC_ROOTmatches the Nginxaliasdirectory. - Check that the Nginx worker can read the files and traverse their parent directories.
- Ensure the requested URL begins with the configured static prefix, such as
/static/.
Database connection errors
- Test DNS or network reachability from the application host to the configured PostgreSQL host and port.
- Verify the database name, role, password and permissions.
- Confirm the PostgreSQL server accepts connections from the application host.
- Check that the installed Django version supports the PostgreSQL version and psycopg version you selected.
- Review pool sizing if errors appear only under concurrency; the combined worker and pool count may exceed PostgreSQL’s connection budget.
HTTPS, redirects or CSRF failures
- Verify that Nginx forwards
X-Forwarded-Protoand that Django trusts only the real proxy chain. - Check the exact hostname and scheme in
ALLOWED_HOSTSand CSRF trusted origins. - Confirm secure cookies are not being tested over plain HTTP.
- Look for a second proxy or load balancer that may overwrite forwarded headers.
WebSockets do not connect
- Confirm the application actually exposes a WebSocket route and that the selected worker supports the ASGI workload.
- Verify Nginx sends
Upgradeand the controlledConnectionvalue. - Check idle timeout settings at every proxy layer.
- Ensure forwarded scheme and host values remain consistent when TLS terminates before Nginx.
Operational practices after launch
Track process restarts, response errors, latency, database connection use and disk space for collected files and logs. Keep Nginx and application logs distinguishable so a proxy failure can be separated from a Django exception. Test the deployment procedure on a staging environment, including migrations and rollback behavior.
Back up PostgreSQL on a schedule that matches the application’s recovery objectives and periodically perform a restore into an isolated environment. Treat schema migrations as release changes: review them, apply them in the intended order and preserve a rollback or forward-fix plan before changing production traffic.
When scaling, increase worker counts and database pool sizes independently and measure the resulting PostgreSQL connection demand. A larger ASGI worker pool does not automatically improve throughput if queries, external services or the database are the bottleneck.
Quick Recap
Production readiness checklist
- Production settings load without relying on a developer shell.
- Secrets are stored outside source control.
- PostgreSQL uses the supported version and psycopg driver for the installed Django release.
- The application role has only required database permissions.
migratecompleted against the intended database.collectstatic --noinputpopulated the directory served by Nginx.- Gunicorn runs
myproject.asgi:applicationwithuvicorn_worker.UvicornWorkerunder a supervisor. - Nginx proxies through the intended socket and forwards host and scheme headers.
- WebSocket headers are present only when required and are tested end to end.
- HTTPS redirects work, cookies are secure and CSRF origins are exact.
python manage.py check --deployhas no unresolved warnings.- Health checks, logs, backups and restore procedures are operational.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

