> ## 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.

# Run FastAPI background tasks

> Run short FastAPI tasks after the response with BackgroundTasks, and longer ones with Celery in a Sevalla background worker, using a Redis database as the broker.

FastAPI has two ways to run work outside the request. `BackgroundTasks` runs a function in the web process right after it sends the response: fine for a few seconds of work, but the task is lost if the instance restarts, and it shares the web process's CPU. For anything longer, Celery runs the tasks in a separate background worker process on Sevalla, with a Redis database holding the queue.

## Before you begin

* A FastAPI project deployed on Sevalla, as in [Deploy FastAPI](/guides/python/fastapi/deploy).

## Run a short task after the response

Add a `BackgroundTasks` parameter to a route and queue the function on it. It needs no setup on Sevalla:

```python storefront/main.py theme={null}
from fastapi import BackgroundTasks


def write_audit_log(message: str) -> None:
    print(message)


@app.post("/orders/{order_id}/cancel")
def cancel_order(order_id: int, background_tasks: BackgroundTasks):
    background_tasks.add_task(write_audit_log, f"Order {order_id} cancelled")
    return {"status": "cancelled"}
```

## Create a Redis database

<Steps>
  <Step title="Create the database">
    In the sidebar, click **Databases**, then click **Create database**. Under **Database**, choose **Redis**, or **Valkey**: the code in this guide works with both. Enter `storefront-redis` as the **Display name**, choose the same **Data center** as the application and a **Size**, then click **Create database**.
  </Step>

  <Step title="Connect it to the application">
    In the sidebar, click **Applications**, then click the application. Click the **Networking** tab. Under **Connected services**, click **Add internal connection**. Choose `storefront-redis` as the **Service**, turn on **Add environment variables to the application**, and click **Add internal connection**. Sevalla adds `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD` and `REDIS_URL`. See [Connect an application](/databases/connect#connect-an-application).
  </Step>
</Steps>

## Add Celery to the project

Install Celery with its Redis support, and update `requirements.txt`:

```bash theme={null}
pip install "celery[redis]"
pip freeze > requirements.txt
```

Create the Celery application and a task:

```python storefront/worker.py theme={null}
import os

from celery import Celery

# A Valkey database's URL starts with valkey://, which Celery doesn't accept.
broker_url = os.environ.get("REDIS_URL", "redis://localhost:6379/0").replace("valkey://", "redis://", 1)

celery = Celery("storefront", broker=broker_url)
celery.conf.broker_connection_retry_on_startup = True


@celery.task
def send_order_confirmation(order_id: int) -> None:
    print(f"Sending the confirmation for order {order_id}")
```

Queue the task from a route with `delay`:

```python storefront/main.py theme={null}
from storefront.worker import send_order_confirmation


@app.post("/orders/{order_id}/confirm")
def confirm_order(order_id: int):
    send_order_confirmation.delay(order_id)
    return {"status": "queued"}
```

Commit and push the changes.

## Run the worker on Sevalla

<Steps>
  <Step title="Create the background worker">
    On the application's **Processes** tab, click **Create process**. Choose **Background worker**, enter `Celery worker` as the **Name** and this **Custom start command**:

    ```bash theme={null}
    celery -A storefront.worker worker --loglevel INFO --concurrency 2
    ```

    Choose a **Resource** and click **Create process**.
  </Step>

  <Step title="Deploy">
    Click **Deploy** in the undeployed changes bar at the bottom of the page.
  </Step>
</Steps>

Set `--concurrency` to match the size. Without it, Celery starts one worker process per CPU of the machine it runs on, which can be more than the process's memory holds. Two processes fit in **H1** (0.3 GB of memory). To process more tasks at once, add instances in the process's **Scaling** section, or choose a larger size and raise `--concurrency`. See [Scaling](/applications/scaling).

<Frame>
  <img className="block dark:hidden" src="https://assets.sevalla.com/screenshots/light/app-processes-worker-create.png" alt="The Create process sheet with Background worker selected, the Name and Custom start command fields, the resource and the scaling settings" width="768" />

  <img className="hidden dark:block" src="https://assets.sevalla.com/screenshots/dark/app-processes-worker-create.png" alt="The Create process sheet with Background worker selected, the Name and Custom start command fields, the resource and the scaling settings" width="768" />
</Frame>

## Run tasks on a schedule

Run scheduled work as a cron job: create a **Cron job** process with a **Schedule** in cron syntax, such as `0 3 * * *` for every day at 03:00 UTC, and a **Custom start command** that runs a script, such as `python -m storefront.cleanup`. See [Cron jobs](/applications/processes#cron-jobs).

## Verify

1. Queue a task, with your application's domain:

   ```bash theme={null}
   curl -X POST https://storefront-x7k2m.sevalla.app/orders/42/confirm
   ```

   The response is `{"status":"queued"}`.
2. Click the **Logs** tab and look at the worker's runtime logs. They show `Task storefront.worker.send_order_confirmation[...] received`, the line `Sending the confirmation for order 42`, and `Task storefront.worker.send_order_confirmation[...] succeeded`.

On the **Processes** tab, the `Celery worker` process shows **Running**. If it shows **Crashed**, the logs show why, for example `Error 111 connecting` when `REDIS_URL` is missing or wrong.


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