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

# Use object storage from FastAPI

> Upload files from FastAPI to a Sevalla object storage bucket with boto3 and the S3 API, list them, and share them with presigned URLs.

An application's filesystem on Sevalla resets on every deployment, so files that users upload belong in a Sevalla [object storage](/object-storage/overview) bucket. FastAPI talks to the bucket through its S3-compatible API with boto3, the AWS SDK for Python.

## Before you begin

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

## Create a bucket

In the sidebar, click **Object storage**, then click **Create object storage**. Enter `storefront-media` as the **Name**, choose the **Location** closest to the application, and click **Create object storage**. Keep it private: the code in this guide shares each file through a presigned URL that expires.

## Add the bucket details to the application

The values come from the bucket's **Settings** tab. See [Connect with the S3 API](/object-storage/connect) for each one.

| Variable | Where to find it |
| - | - |
| `S3_BUCKET` | **Bucket name** under **Details**, for example `storefront-media-q8w2e`. It's not the display name. |
| `S3_ENDPOINT` | **Endpoint** under **Details**, in the form `https://<account-id>.r2.cloudflarestorage.com` |
| `S3_ACCESS_KEY_ID` | **Access key** under **Access keys** |
| `S3_SECRET_ACCESS_KEY` | **Secret key** under **Access keys** |

<Frame>
  <img className="block dark:hidden" src="https://assets.sevalla.com/screenshots/light/object-storage-settings-details.png" alt="A bucket's Settings tab with the display name, location, bucket name, ID and endpoint, and the access key and secret key" width="1216" />

  <img className="hidden dark:block" src="https://assets.sevalla.com/screenshots/dark/object-storage-settings-details.png" alt="A bucket's Settings tab with the display name, location, bucket name, ID and endpoint, and the access key and secret key" width="1216" />
</Frame>

<Steps>
  <Step title="Open the environment variables">
    In the sidebar, click **Applications**, then click the application. Click the **Environment variables** tab.
  </Step>

  <Step title="Add the variables">
    Click **Add environment variables**. Add the variables, keep **Available during runtime** selected, and click **Add environment variables**.
  </Step>
</Steps>

The variables apply with the next deployment. Add them before you deploy the code: the application doesn't start without them.

## Install boto3

Install boto3 and update `requirements.txt`:

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

## Add the S3 client

Create the client from environment variables. Cloudflare R2, which runs Sevalla's object storage, uses the region `auto`:

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

import boto3

BUCKET = os.environ["S3_BUCKET"]

s3 = boto3.client(
    "s3",
    endpoint_url=os.environ["S3_ENDPOINT"],
    aws_access_key_id=os.environ["S3_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["S3_SECRET_ACCESS_KEY"],
    region_name="auto",
)
```

## Upload and list files

Add two routes to `storefront/main.py`. Uploads use FastAPI's `UploadFile`, which `fastapi[standard]` supports. The routes are plain `def` functions, so FastAPI runs boto3's blocking calls in a thread pool:

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

from storefront.storage import BUCKET, s3


@app.post("/files")
def upload_file(file: UploadFile):
    s3.upload_fileobj(
        file.file,
        BUCKET,
        file.filename,
        ExtraArgs={"ContentType": file.content_type or "application/octet-stream"},
    )
    return {"filename": file.filename}


@app.get("/files")
def list_files():
    response = s3.list_objects_v2(Bucket=BUCKET)
    return [
        {
            "filename": item["Key"],
            "url": s3.generate_presigned_url(
                "get_object",
                Params={"Bucket": BUCKET, "Key": item["Key"]},
                ExpiresIn=3600,
            ),
        }
        for item in response.get("Contents", [])
    ]
```

Each URL works for 1 hour. `list_objects_v2` returns up to 1,000 objects per call; pass `ContinuationToken` for more. See [presigned URLs](/object-storage/connect#share-files-with-presigned-urls). For a public bucket, link files on its public domain instead; see [public URLs](/object-storage/connect#public-urls).

Commit and push the changes. If **Auto-deploy** is off, click **Deploy now** on the application's **Overview** tab.

## Verify

Upload a file and list the bucket, with your application's domain:

```bash theme={null}
echo "Hello from Sevalla" > hello.txt
curl -F "file=@hello.txt" https://storefront-x7k2m.sevalla.app/files
curl https://storefront-x7k2m.sevalla.app/files
```

The first response is `{"filename":"hello.txt"}`. The second lists `hello.txt` with a `url`: open it, and the page reads `Hello from Sevalla`. In the sidebar, click **Object storage**, click the bucket, and the **Objects** tab lists `hello.txt`. See [Manage objects](/object-storage/objects).


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