FastAPI runs on Hangar as a long-lived uvicorn server in a container. WebSockets, streaming responses and background tasks work as they do on your machine.
Before you start
- A FastAPI app in a Git repository, with its dependencies in
requirements.txtorpyproject.toml:
fastapi
uvicorn[standard]
1. Create the service
Create a project, add a service and pick your repository and branch. Choose the region closest to your users, and leave the builder on Automatic: it detects Python and installs your dependencies, with pip, uv or Poetry depending on the files in the repository.
2. Set the start command
In the service's Settings → Runtime, set the start command:
uvicorn main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips=*
main:appis the module and the variable that holds yourFastAPI()instance. Adjust it to your layout, for exampleapp.main:app.--host 0.0.0.0is required. On127.0.0.1the proxy cannot reach the app.--proxy-headersmakes the app see the original scheme and client address, since Hangar terminates TLS in front of it.
To use more than one CPU core, add --workers 2 and raise the number with the service's CPU limit.
3. Deploy and add a domain
Deploy the service. In Settings → Networking, add a domain with container port 8000 and HTTPS on. Your interactive API docs are then at /docs on that domain.
Adding PostgreSQL
Add a PostgreSQL service in the same project and region, copy its internal connection URL and set it on the app:
DATABASE_URL=<the internal connection URL>
Read it in your code:
import os
from sqlalchemy import create_engine
engine = create_engine(os.environ["DATABASE_URL"], pool_pre_ping=True)
With an async driver, replace the scheme of the URL, for example postgresql+asyncpg://.
Migrations
Run Alembic before the server, so each release migrates first. Chained commands need a shell: set the start command to sh and add two arguments, -c and this line:
alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips="*"
Inside the shell the quotes around * matter; in the plain start command above they must be left out. See Start command.
Background workers
Celery, ARQ or RQ workers run as a second service from the same repository, with their own start command and a Redis database as the broker. See Databases.
Troubleshooting
- 502 on the domain: uvicorn is bound to
127.0.0.1, or the container port is not8000. - Redirects go to
http://:--proxy-headersis missing. ModuleNotFoundErrorat start: the module path in the start command does not match the repository layout, or the service's root directory is wrong.