How to run UVICORN in Heroku?
The answer(s) are correct, but to use FastAPI in production running as WSGI with ASGI workers is a better choice here is why, i ran a benchmark for this question, so here is the results.
Gunicorn with Uvicorn workers
Requests per second: 8665.48 [#/sec] (mean)
Concurrency Level: 500
Time taken for tests: 0.577 seconds
Complete requests: 5000
Time per request: 57.700 [ms] (mean)
Pure Uvicorn
Requests per second: 3200.62 [#/sec] (mean)
Concurrency Level: 500
Time taken for tests: 1.562 seconds
Complete requests: 5000
Time per request: 156.220 [ms] (mean)
As you can see there is a huge difference in RPS(Request per second) and response time for each request.
Procfiles
Gunicorn with Uvicorn Workers
web: gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
Pure uvicorn
web: uvicorn main:app --workers 4
You can also configure your FastAPI to run on Gunicorn
with uvicorn
as worker process. Following is the command line you can keep in the Procfile used by Heroku to make your app up and running. The below command will spin up your app on 3 worker processes
web: gunicorn -w 3 -k uvicorn.workers.UvicornWorker main:app
For detailed step by step video you can visit this video tutorial that details how to deploy FastAPI on Heroku in just 6 minutes. or you can have a detailed walkthrough of how to create and deploy python based FastAPI on Heroku from this blog post.
I've tested your setup and after some checking (never used Heroku before) I'm guessing your uvicorn never binds to the appointed port (was the heroku-cli command heroku local
working for you?)
Your Procfile could look like this;
web: uvicorn src.main:app --host=0.0.0.0 --port=${PORT:-5000}
This example assumes you have your source code within a subfolder named 'src' which has an empty __init__.py
(indicating a Python module, you probably want to add src to the PYTHONPATH instead, see app.json) and main.py
containing your fastapi app;
import socket
import sys
from fastapi import FastAPI
app = FastAPI()
hostname = socket.gethostname()
version = f"{sys.version_info.major}.{sys.version_info.minor}"
@app.get("/")
async def read_root():
return {
"name": "my-app",
"host": hostname,
"version": f"Hello world! From FastAPI running on Uvicorn. Using Python {version}"
}
I have added this example to github which you can view on heroku (for now)