Skip to Content

Deploy a FastAPI App ⚡

Push your FastAPI project to GitHub or GitLab and Faable builds and runs it — no Dockerfile, no YAML, no uvicorn command to write. The builder detects FastAPI from your repo, installs your dependencies, and serves it behind automatic SSL at https://<app>.faable.link, hosted in Europe.

What Faable detects

Detection is file-based (full rules). For FastAPI the builder needs:

It looks forWhich gives it
requirements.txt, pyproject.toml or PipfileYour dependencies — installed into a virtualenv
A module defining app = FastAPI(...)The ASGI entrypoint → the start command below

It searches for that module in this order, preferring the file that actually defines app:

main.py → app.py → asgi.py → wsgi.py → application.py → server.py → app/main.py → app/app.py → src/main.py

Then it runs, with no configuration from you:

uvicorn <module>:app --host 0.0.0.0 --port $PORT

uvicorn is installed automatically if it isn’t in your dependencies — though pinning it is good practice.

The one thing to get right: $PORT

Faable assigns your app a port at runtime and passes it as the PORT environment variable. The auto-detected command already binds 0.0.0.0 and $PORT, so a standard project needs no change. It matters only if you override the start command:

# ✅ Correct — for local runs; on Faable the platform passes the port import os import uvicorn if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=int(os.environ.get("PORT", 8000)))

Never hardcode port=8000 in the command Faable runs — the app would start and receive no traffic.

A minimal deployable app

# main.py from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"status": "ok"} @app.get("/healthz") def healthz(): return {"ok": True}
# requirements.txt fastapi uvicorn[standard]

That is a complete, deployable repo — two files.

Configuration and secrets

Read configuration from the environment and set it with the CLI or the dashboard:

import os DATABASE_URL = os.environ["DATABASE_URL"] ALLOWED_ORIGINS = os.environ.get("ALLOWED_ORIGINS", "").split(",")
faable deploy secrets set DATABASE_URL=postgres://… ALLOWED_ORIGINS=https://myapp.faable.link

Faable also injects PORT, FAABLE_APP_ID, FAABLE_RELEASE and FAABLE_GIT_COMMIT — useful for /healthz payloads and error reports. See Environment & Releases.

Deploy

From the dashboard — the normal path:

  1. Create a Project and an App in the Faable Dashboard .
  2. Click Link repository and pick your FastAPI repo.
  3. Push to your release branch. Faable builds and takes it live.

Or from your laptop:

npm i -g @faable/faable faable login faable deploy

Your API is live at https://<app>.faable.link with automatic SSL and the WAF already inspecting traffic. Interactive docs are at /docs if you left them enabled.

Workers and concurrency

The auto-detected command runs a single uvicorn process, and that is the right setting: FastAPI is async, so one process handles many concurrent requests. Leave the worker count to Faable and don’t add --workers to your start command — every worker is a separate process with its own copy of your app and its libraries, so each one multiplies the memory the app needs. To scale up, pick a bigger instance. On the Free plan an app runs at most one worker — see Workers and memory.

Choosing the Python version

A new app gets the newest supported minor — Python 3.14 today — and keeps the minor of its first successful deploy from then on. Supported minors are 3.10, 3.11, 3.12, 3.13 and 3.14 — pin one with a runtime.txt:

python-3.12

Or with .python-version, or requires-python in pyproject.toml. First match wins, in that order.

Troubleshooting

  • Build fails with “no start command” — the builder installed dependencies but found no module defining app = FastAPI(...) in the search list. Rename your entrypoint to main.py, or set startCommand in faable.json.
  • The app starts but requests time out — the start command binds a hardcoded port instead of $PORT.
  • CORS errors from your frontend — add CORSMiddleware and drive its origins from an environment variable.
  • ModuleNotFoundError at boot — the dependency isn’t in requirements.txt; the build environment installs only what the manifest declares.
  • A Dockerfile is being used instead of the Python buildpack — detection order puts package.json (Node) above Python and Python above Dockerfile. Force it with {"buildpack": "python"} in faable.json.

FAQ

Do I need a Dockerfile to deploy FastAPI on Faable?

No. Faable finds the module that defines app = FastAPI(...) and generates the uvicorn command for you. A Dockerfile is the escape hatch for stacks the buildpacks don’t detect natively.

How does Faable know which file my FastAPI app is in?

It checks main.py, app.py, asgi.py, wsgi.py, application.py, server.py, app/main.py, app/app.py and src/main.py, preferring the one that actually defines app. If yours lives elsewhere, set startCommand in faable.json.

Can I run FastAPI with multiple uvicorn workers?

You rarely need to. One async process serves many concurrent requests, and each extra worker loads another full copy of your app into memory. Leave the worker count to the command Faable generates, and move to a larger instance for more capacity.

Which Python versions does Faable support for FastAPI?

3.10, 3.11, 3.12, 3.13 and 3.14. A new app gets the newest (3.14 today) and keeps the minor of its first deploy; pin your choice with runtime.txt (python-3.13), .python-version, or requires-python in pyproject.toml.

Does the built-in WAF interfere with API clients?

No. The firewall matches paths, user-agents and query parameter names, never request bodies, so JSON payloads from API clients are not inspected. What it blocks by default are scanner paths no API serves (.env, .git/, *.php…). If a client of yours does get a 403, faable deploy waf list shows the rules in effect — see the WAF guide.

Last updated on