> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are the docs for Sevalla, a platform for deploying applications, databases, static sites, object storage and load balancers. The dashboard is at https://app.sevalla.com.
> Bold text in steps is an exact dashboard label.
> To act on a Sevalla account, prefer the Sevalla MCP server (https://mcp.sevalla.com/mcp, see https://docs.sevalla.com/ai-agents/overview.md), then the Sevalla CLI (https://docs.sevalla.com/developer-tools/cli.md), then the REST API (https://api-docs.sevalla.com, OpenAPI spec at https://api.sevalla.com/v3/openapi.json).
> Every page index is at https://docs.sevalla.com/llms.txt.

# Deploy FastAPI

> Deploy a FastAPI application to Sevalla from a Git repository, with the FastAPI CLI, SQLModel on a PostgreSQL database and Alembic migrations in a job.

A FastAPI project runs on Sevalla as an application built from your Git repository: Nixpacks installs it, `fastapi run` serves it with Uvicorn in the web process, and a job runs its Alembic migrations before each deployment. Its data lives in a Sevalla PostgreSQL database that the application reaches over an internal connection.

## Before you begin

* A Sevalla company with a payment method, and a GitHub, GitLab or Bitbucket account for the repository. See [Git providers](/git/overview).
* Python 3.12 or later on your computer.

## Create the project

Create a virtual environment and install FastAPI, SQLModel, Alembic and the PostgreSQL driver:

```bash theme={null}
mkdir storefront && cd storefront
python3 -m venv .venv
source .venv/bin/activate
pip install "fastapi[standard]" sqlmodel alembic "psycopg[binary]"
mkdir storefront && touch storefront/__init__.py
```

| Package | What it does |
| - | - |
| `fastapi[standard]` | FastAPI with the `fastapi` command, Uvicorn and form and file upload support |
| `sqlmodel` | Models and sessions on top of SQLAlchemy |
| `alembic` | Database migrations |
| `psycopg[binary]` | PostgreSQL driver |

Keep local files out of the repository:

```text .gitignore theme={null}
.venv/
__pycache__/
.env
*.db
```

Add the database engine. Sevalla's connection URLs start with `postgres://`, which SQLAlchemy doesn't accept, so the code switches the scheme to `postgresql+psycopg://`:

```python storefront/db.py theme={null}
import os
from typing import Annotated

from fastapi import Depends
from sqlmodel import Session, create_engine


def database_url() -> str:
    url = os.environ.get("DB_URL", "sqlite:///storefront.db")
    return url.replace("postgres://", "postgresql+psycopg://", 1)


engine = create_engine(database_url())


def get_session():
    with Session(engine) as session:
        yield session


SessionDep = Annotated[Session, Depends(get_session)]
```

`DB_URL` is the variable Sevalla adds when you connect the database; without it, the project uses SQLite locally.

Add a model and the application:

```python storefront/models.py theme={null}
from sqlmodel import Field, SQLModel


class Item(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
```

```python storefront/main.py theme={null}
from fastapi import FastAPI
from sqlmodel import select

from storefront.db import SessionDep
from storefront.models import Item

app = FastAPI()


@app.get("/")
def index(session: SessionDep):
    items = session.exec(select(Item)).all()
    return {"items": len(items)}


@app.post("/items")
def create_item(item: Item, session: SessionDep) -> Item:
    session.add(item)
    session.commit()
    session.refresh(item)
    return item
```

## Prepare the project for Sevalla

### Set up migrations

Create the Alembic files:

```bash theme={null}
alembic init alembic
```

In `alembic/env.py`, point Alembic at the same database URL and at the SQLModel tables. Replace the `config` and `target_metadata` lines:

```python alembic/env.py theme={null}
from sqlmodel import SQLModel

import storefront.models  # noqa: F401 (registers the tables)
from storefront.db import database_url

config = context.config
# Alembic's config treats % as a special character: passwords in URLs can contain it.
config.set_main_option("sqlalchemy.url", database_url().replace("%", "%%"))

target_metadata = SQLModel.metadata
```

SQLModel columns use its own types, so add the import to the template that Alembic writes migrations from:

```python alembic/script.py.mako theme={null}
from alembic import op
import sqlalchemy as sa
import sqlmodel
```

Generate the first migration and apply it to the local database:

```bash theme={null}
alembic revision --autogenerate -m "Create item table"
alembic upgrade head
fastapi dev storefront/main.py
```

### Pin the Python version

Nixpacks, the default [build strategy](/applications/builds/nixpacks), reads the Python version from `.python-version`. Nixpacks 1.41 supports Python versions up to 3.13, so pin 3.13:

```text .python-version theme={null}
3.13
```

### Set the start command

Create `nixpacks.toml` in the repository root:

```toml nixpacks.toml theme={null}
[start]
cmd = "fastapi run storefront/main.py --port $PORT --forwarded-allow-ips '*'"
```

* `fastapi run` starts Uvicorn in production mode on `0.0.0.0`. Requests reach it through Sevalla's ingress, so `--forwarded-allow-ips '*'` makes Uvicorn trust the `X-Forwarded-Proto` and `X-Forwarded-For` headers, and FastAPI sees the original scheme and client address. See [Request headers](/applications/http-headers).
* The web process listens on the port in the `PORT` environment variable. Sevalla sets `PORT` to the web process's port, `8080` by default.
* To use more CPU, add `--workers 2` (or more) when you choose a larger size.
* Migrations don't run here. A [job](/applications/processes#jobs) runs them once per deployment.

### List the dependencies

Nixpacks installs `requirements.txt` with pip:

```bash theme={null}
pip freeze > requirements.txt
```

Commit everything, including the `alembic` folder and `alembic.ini`, and push it to your Git provider:

```bash theme={null}
git init
git add .
git commit -m "Prepare for Sevalla"
git branch -M main
git remote add origin https://github.com/acme/storefront.git
git push -u origin main
```

## Deploy to Sevalla

<Steps>
  <Step title="Create the database">
    In the sidebar, click **Databases**, then click **Create database**. Under **Database**, choose **PostgreSQL** and the newest version. Enter `orders-db` as the **Display name**, choose a **Data center** and a **Size**, then click **Create database**. See [Create a database](/databases/create) for every field.
  </Step>

  <Step title="Create the application">
    In the sidebar, click **Applications**, then click **Create** > **Application**. Under **Source**, click **Private repository**, then choose the **Git provider**, the **Repository** and the **Branch**. Under **Location**, choose the same **Data center** as the database. Keep the **Size** at **H1** or choose a larger one, then click **Create application**. The application opens on its **Overview** tab with the status **Not deployed yet**: creating an application doesn't deploy it.
  </Step>

  <Step title="Connect the database">
    Click the **Networking** tab. Under **Connected services**, click **Add internal connection**. Choose `orders-db` as the **Service**, turn on **Add environment variables to the application**, and click **Add internal connection**. Sevalla adds `DB_URL` and the other connection variables to the application. See [Connect an application](/databases/connect#connect-an-application).
  </Step>

  <Step title="Create the migration job">
    Click the **Processes** tab, then click **Create process**. Choose **Job**, enter `Migrate` as the **Name** and `alembic upgrade head` as the **Custom start command**. Keep the **Start policy** at **Before deployment** and the **Resource** at **H1**, then click **Create process**.
  </Step>

  <Step title="Deploy">
    Click the **Overview** tab, then click **Deploy now** under **Recent deployments**. Check the **Branch** and click **Deploy**.
  </Step>

  <Step title="Turn on auto-deploy">
    To deploy every push to the branch automatically, click the **Settings** tab, then click **Update auto-deploy** in the **Auto-deploy** section. Turn on **Auto-deploy** and click **Update auto-deploy**. See [Deployments](/applications/deployments#deploy-automatically-on-push).
  </Step>
</Steps>

<Frame>
  <img className="block dark:hidden" src="https://assets.sevalla.com/screenshots/light/app-processes-job-create.png" alt="The Create process sheet with Job selected, the Name and Custom start command fields, the Start policy set to Before deployment, and the H1 resource" width="768" />

  <img className="hidden dark:block" src="https://assets.sevalla.com/screenshots/dark/app-processes-job-create.png" alt="The Create process sheet with Job selected, the Name and Custom start command fields, the Start policy set to Before deployment, and the H1 resource" width="768" />
</Frame>

## Verify the deployment

1. On the **Deployments** tab, wait for the deployment's status to change to **Deployed**. The application's status next to its name changes to **Running**. During the rollout, the deployment's build log shows `Migrate has been successful`. If the job fails, the deployment fails and nothing changes for visitors.
2. Click the domain under the application's name. The response is `{"items":0}`, which shows that the application reads the PostgreSQL database.
3. Add an item and read it back, with your application's domain:

   ```bash theme={null}
   curl -X POST https://storefront-x7k2m.sevalla.app/items \
     -H "Content-Type: application/json" \
     -d '{"name": "Mug"}'
   curl https://storefront-x7k2m.sevalla.app/
   ```

   The second response is `{"items":1}`. The interactive API docs are at `/docs`.

## Deploy changes

With **Auto-deploy** on, every push to the branch starts a deployment; otherwise, click **Deploy now** for each change. Each deployment builds the image, runs the `Migrate` job, then replaces the web process's instances. For a schema change, run `alembic revision --autogenerate` locally and commit the new migration with the code. See [Deployments](/applications/deployments).

## Add a custom domain

[Add the domain](/domains/overview) on the application's **Domains** tab. FastAPI doesn't check the host, so no code change is needed. Custom domains need a web process larger than **H1**.

## Troubleshoot the deployment

| Symptom | Cause | Fix |
| - | - | - |
| The application or the job crashes with `Can't load plugin: sqlalchemy.dialects:postgres` | The code passes Sevalla's `postgres://` URL to SQLAlchemy unchanged. | Replace the scheme with `postgresql+psycopg://`, as `database_url()` does. |
| The job crashes with `invalid interpolation syntax` | The database password contains `%`, and Alembic's config reads it as a placeholder. | Escape it with `.replace("%", "%%")` in `alembic/env.py`. |
| The deployment fails at `Migrate has failed` | The migration raised an error, or the application can't reach the database. | Read the job's output in the build log. Check that the internal connection exists and that `DB_URL` is set. |
| The web process crashes with `Path does not exist` | The path in the start command doesn't match the file with the `FastAPI` app. | Update `[start]` in `nixpacks.toml`, for example `fastapi run app/main.py --port $PORT --forwarded-allow-ips '*'`. |
| The build fails with `Could not find a version that satisfies the requirement` | The build uses a Python version older than a package supports. | Add `.python-version` with `3.13`. |

For more causes, see [Troubleshoot builds and deployments](/applications/troubleshooting/builds-and-deployments). To add file storage, caching or background tasks, see the [FastAPI guides](/guides/python/fastapi/overview).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.