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

> Deploy a Django application to Sevalla from a Git repository, with Gunicorn, WhiteNoise for static files, a PostgreSQL database and migrations in a job.

A Django project runs on Sevalla as an application built from your Git repository: Nixpacks installs it, Gunicorn serves it in the web process, WhiteNoise serves its static files, and a job runs its 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. Django 6.1 needs Python 3.12 or later.

## Create the project

Create a virtual environment, install Django and the packages the project needs in production, and start a project named `storefront`:

```bash theme={null}
mkdir storefront && cd storefront
python3 -m venv .venv
source .venv/bin/activate
pip install django django-environ gunicorn whitenoise "psycopg[binary]"
django-admin startproject storefront .
```

| Package | What it does |
| - | - |
| `django-environ` | Reads settings from environment variables and database URLs |
| `gunicorn` | Production WSGI server for the web process |
| `whitenoise` | Serves static files from the application |
| `psycopg[binary]` | PostgreSQL driver |

Keep local files out of the repository:

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

For local development, put development values in `.env`. Sevalla doesn't use this file: you set the production values as environment variables.

```bash .env theme={null}
DEBUG=True
SECRET_KEY=local-development-only
```

## Prepare the project for Sevalla

### Read settings from environment variables

Replace the top of `storefront/settings.py` and the `DATABASES` setting so that the secret key, debug mode, hosts and database come from the environment:

```python storefront/settings.py theme={null}
import os
from pathlib import Path

import environ

BASE_DIR = Path(__file__).resolve().parent.parent

env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env(os.path.join(BASE_DIR, ".env"))

SECRET_KEY = env("SECRET_KEY")
DEBUG = env("DEBUG")
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["localhost", "127.0.0.1"])
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])

# Sevalla terminates HTTPS and forwards the original scheme in X-Forwarded-Proto.
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

# ...keep INSTALLED_APPS, MIDDLEWARE, TEMPLATES and the rest...

DATABASES = {
    "default": env.db("DB_URL", default="sqlite:///db.sqlite3"),
}
```

`DEBUG` is `False` unless you set it. `DB_URL` is the variable Sevalla adds when you connect the database; without it, the project uses SQLite locally. django-environ accepts the `postgres://` URL that Sevalla generates.

### Serve static files with WhiteNoise

Add WhiteNoise right after `SecurityMiddleware`, and tell Django where to collect static files and how to store them:

```python storefront/settings.py theme={null}
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    # ...the rest of the default middleware...
]

STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}
```

WhiteNoise compresses the files and gives them hashed names, so browsers and the [Sevalla CDN](/applications/caching) can cache them for a long time.

### 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 build and start commands

Create `nixpacks.toml` in the repository root:

```toml nixpacks.toml theme={null}
[phases.build]
cmds = ["python manage.py collectstatic --noinput"]

[start]
cmd = "gunicorn storefront.wsgi --bind 0.0.0.0:$PORT --workers 2"
```

* The build runs `collectstatic`, so the static files are part of the image.
* The web process starts Gunicorn on the port in the `PORT` environment variable. Sevalla sets `PORT` to the web process's port, `8080` by default.
* Migrations don't run here. Without a `[start]` command, Nixpacks starts Django with `python manage.py migrate && gunicorn storefront.wsgi`, which migrates every time an instance starts. A [job](/applications/processes#jobs) runs them once per deployment instead.

Two Gunicorn workers fit in the smallest size (**H1**, 0.3 GB of memory). Raise `--workers` when you choose a larger size.

### List the dependencies

Nixpacks installs `requirements.txt` with pip:

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

Commit everything 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="Add the environment variables">
    Click the **Environment variables** tab, then click **Add environment variables**. Add these variables, keep **Available during runtime** and **Available during buildtime** selected, and click **Add environment variables**:

    | Variable | Value |
    | - | - |
    | `SECRET_KEY` | A random string of 50 characters or more. Generate one with `python -c "import secrets; print(secrets.token_urlsafe(50))"`. |
    | `ALLOWED_HOSTS` | The application's domain without the scheme, for example `storefront-x7k2m.sevalla.app`. It's under the application's name in the page header. Separate several hosts with commas. |
    | `CSRF_TRUSTED_ORIGINS` | The same domain with the scheme, for example `https://storefront-x7k2m.sevalla.app`. |

    The build needs these variables too, because `collectstatic` loads the settings. Don't set `DEBUG`: it defaults to `False`.
  </Step>

  <Step title="Create the migration job">
    Click the **Processes** tab, then click **Create process**. Choose **Job**, enter `Migrate` as the **Name** and `python manage.py migrate` 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**. The deployment's build log shows `collectstatic` copying the static files and, during the rollout, `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, then add `/admin/` to the address. Django's admin sign-in page opens with its styles, which shows that WhiteNoise serves the static files. The site root returns `404` until you add your own URLs, because the welcome page only shows with `DEBUG` on.
3. To sign in to the admin, create a superuser. Click the **Web terminal** tab, choose the **Web** process, and run:

   ```bash theme={null}
   . /opt/venv/bin/activate
   python manage.py createsuperuser
   ```

   Nixpacks installs the packages in the virtual environment at `/opt/venv`. See [Web terminal](/applications/web-terminal).

## 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 `python manage.py makemigrations` 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, then add it to `ALLOWED_HOSTS` and `CSRF_TRUSTED_ORIGINS`, for example `storefront-x7k2m.sevalla.app,shop.example.com` and `https://storefront-x7k2m.sevalla.app,https://shop.example.com`. Custom domains need a web process larger than **H1**.

## Troubleshoot the deployment

| Symptom | Cause | Fix |
| - | - | - |
| The build fails with `Set the SECRET_KEY environment variable` | `collectstatic` loads the settings, and `SECRET_KEY` isn't available during the build. | Update the variable and select **Available during buildtime**. |
| The build fails with `Could not find a version that satisfies the requirement Django` | The build uses a Python version older than Django supports. | Add `.python-version` with `3.13`. |
| Pages return `400 Bad Request`, and the runtime logs show `Invalid HTTP_HOST header` | The domain isn't in `ALLOWED_HOSTS`. | Add the domain to `ALLOWED_HOSTS`, then deploy. |
| Forms return `403` with `CSRF verification failed` | The origin isn't in `CSRF_TRUSTED_ORIGINS`. | Add the domain with `https://` to `CSRF_TRUSTED_ORIGINS`, then deploy. |
| 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 admin has no styles | The static files weren't collected, or WhiteNoise isn't in `MIDDLEWARE`. | Check the `[phases.build]` command in `nixpacks.toml` and the middleware order. |

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


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