FastAPI on a VPS: uvicorn workers, proxy headers and a service that restarts

FastAPI speaks ASGI, and the server that runs it is uvicorn. On a VPS, the production recipe has four pieces: uvicorn with a few processes, a systemd service that keeps it alive, nginx in front with HTTPS, and the application listening only on 127.0.0.1. The basics (running with uvicorn main:app) are already in running a Flask or FastAPI application; what follows is what turns it into a proper service.

From test to service

1 Install into the application’s virtual environment: cd /home/appuser/api
python3 -m venv .venv
.venv/bin/pip install fastapi uvicorn
uvicorn’s documentation lists the variants with extras, if you need more speed; start with the plain one.
2 Test: .venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 2--workers starts several processes. The 2 is an example: each process uses memory. Do not use --reload in production: it is for development only.
3 Create the service in /etc/systemd/system/my-api.service:[Unit]
Description=My API
After=network.target

[Service]
User=appuser
WorkingDirectory=/home/appuser/api
EnvironmentFile=/home/appuser/api/.env
ExecStart=/home/appuser/api/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 2
Restart=on-failure

[Install]
WantedBy=multi-user.target
Then: sudo systemctl daemon-reload and sudo systemctl enable --now my-api.
4 Put nginx in front with the headers that tell the API who the visitor is and whether they came over HTTPS: proxy_set_header Host $host;, proxy_set_header X-Forwarded-For $remote_addr; and proxy_set_header X-Forwarded-Proto $scheme;. The rest of the block is in nginx as a reverse proxy.

The details that make a difference

Topic What to know
The visitor’s address Behind the proxy, the API sees nginx’s IP. uvicorn reads X-Forwarded-For from the addresses it trusts (--forwarded-allow-ips, by default only the server itself, which suits a local nginx).
Automatic documentation FastAPI publishes /docs and /redoc. For a private API, switch them off: FastAPI(docs_url=None, redoc_url=None).
Long tasks A request that takes long holds a process. For heavy work, do it outside the request (a queue), instead of raising the workers.
Websockets FastAPI supports them. For nginx, see WebSockets on a VPS.
Cross-origin requests If a site on another domain calls this API: CORS.
Do not expose uvicorn to the Internet. Listening on 0.0.0.0 with the port open in the firewall skips HTTPS and the proxy. Keep 127.0.0.1 and leave only nginx public. If the API does not answer from outside, follow ports and firewall on a VPS.
Create a health route (for example /health answering “ok”) and use it to know whether the API is alive: with curl, or in a monitoring service. If the service will not start, journalctl -u my-api -n 100 says why. On an unmanaged VPS the maintenance is yours: how far our support goes.

Need a server for your API? Have a look at our VPS plans.

See the VPS servers

SEE ALSO

Running Flask or Django with gunicorn and systemd on a VPS

Nginx as a reverse proxy in front of a container

CORS error between your front end and your API: how to fix it

RECOMMENDED PRODUCT

VPS server with root access

Resources of your own, the OS you choose, reinstall whenever you like. from $8.40/mo (3-year plan, with coupon)

See plans
  • 0 Users Found This Useful
Was this answer helpful?