Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

In 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.

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.py is 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
# 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Collect 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Service-manager responsibilities

  • Set the working directory to the directory containing manage.py.
  • Activate or reference the production virtual environment.
  • Set DJANGO_SETTINGS_MODULE and 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.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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_HOSTS contains only the intended hostnames.
  • DEBUG is 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Complete deployment sequence

  1. 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.
  2. Set secrets and environment: provide DJANGO_SETTINGS_MODULE, SECRET_KEY, database values and allowed hosts through the service environment.
  3. Install dependencies: install Django, psycopg, Uvicorn, uvicorn-worker, Gunicorn and the project’s pinned packages.
  4. Validate imports: run python manage.py check and import myproject.asgi:application.
  5. Configure PostgreSQL: create the UTF-8 database and least-privilege application role, then verify network access and permissions.
  6. Apply schema: run python manage.py migrate.
  7. Collect assets: run python manage.py collectstatic --noinput into the directory Nginx will serve.
  8. Create the service: run Gunicorn with uvicorn_worker.UvicornWorker, bind it to the chosen Unix socket, and enable restart and logging behavior.
  9. Configure Nginx: serve STATIC_ROOT, proxy dynamic requests, forward the required headers and add WebSocket handling only if needed.
  10. Enable HTTPS: configure certificates, redirect HTTP, and verify secure cookie and CSRF settings.
  11. 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.
  12. 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.py and 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 --noinput with the production settings.
  • Confirm STATIC_ROOT matches the Nginx alias directory.
  • 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-Proto and that Django trusts only the real proxy chain.
  • Check the exact hostname and scheme in ALLOWED_HOSTS and 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 Upgrade and the controlled Connection value.
  • 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.

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.
  • migrate completed against the intended database.
  • collectstatic --noinput populated the directory served by Nginx.
  • Gunicorn runs myproject.asgi:application with uvicorn_worker.UvicornWorker under 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 --deploy has 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.