Skip to main content
Django runs async def views under any server, but under a WSGI server, such as Gunicorn with its default workers, each async view runs in a one-off event loop, without the concurrency it’s written for. Served over ASGI, async views run on an event loop. On Sevalla, you switch the web process’s start command from the WSGI entry point to asgi.py, served by Gunicorn with Uvicorn workers.

Before you begin

Install Uvicorn

Install Uvicorn and its Gunicorn worker class, and update requirements.txt:
The worker class comes from the uvicorn-worker package: uvicorn.workers.UvicornWorker in Uvicorn itself is deprecated.

Add an async view

Add a view that awaits, and that reports which kind of request object Django built:
storefront/views.py
storefront/urls.py
In async views, use the ORM’s async methods, such as await Product.objects.aget(pk=1) and async for product in Product.objects.all(), or wrap synchronous code in asgiref.sync.sync_to_async.

Serve the project over ASGI

Point the start command in nixpacks.toml at asgi.py, with the Uvicorn worker class:
nixpacks.toml
Gunicorn manages the worker processes and restarts them if they crash; each worker runs Uvicorn’s event loop. The web process still listens on the port in the PORT environment variable, 8080 by default. If you set a Custom start command on the web process, it overrides nixpacks.toml: update it there instead, on the Processes tab: click ⋯ on the Web process, then click Update process. Commit and push the changes. If Auto-deploy is off, click Deploy now on the application’s Overview tab.

Verify

When the deployment’s status is Deployed, request the view, with your application’s domain:
The response is {"interface": "ASGIRequest"}. Under the WSGI start command, the same view returns {"interface": "WSGIRequest"}. The web process’s runtime logs, on the application’s Logs tab, include Gunicorn’s Using worker: uvicorn_worker.UvicornWorker line. See Logs.