Framework guides
Django
Deploy Django, then add object storage, caching, async views, Celery, health checks and multitenancy.
FastAPI
Deploy FastAPI with SQLModel and Alembic, then add object storage, caching, Celery and health checks.
Flask
Deploy Flask with an application factory, then add caching, Celery and health checks.
Streamlit
Deploy a Streamlit data app with uv, including file uploads, secrets and health checks.
Wagtail
Deploy a Wagtail site on Django with PostgreSQL, images and documents in object storage, and the admin over HTTPS.
Litestar
Deploy a Litestar API with Uvicorn, PostgreSQL through Advanced Alchemy, and migrations in a job.
Dash
Deploy a Plotly Dash app with Gunicorn, and scale its callbacks across instances.
MkDocs
Deploy an MkDocs site with Material as a free static site built with uv, or as an application from a Dockerfile.
Gradio
Deploy a Gradio app with uv, and call it from Python with
gradio_client.How Python applications run on Sevalla
Every process (web, worker, cron job and job) runs from the same image, each with its own start command. See Processes for every process type and its settings.
Pin the Python version
Ingot builds on Python 3.14, its default line, unless the project sets a version. Pin the version you develop with in.python-version, so the build doesn’t change when a new Ingot release moves its default line:
.python-version
INGOT_PYTHON_VERSION environment variable, versions.python in ingot.json, .python-version, .tool-versions, mise’s configuration, runtime.txt, requires-python in pyproject.toml (or Poetry’s python dependency), then python_version in the Pipfile. A line such as 3.14 builds on that line’s newest release, so every new build picks up its security fixes. The build plan’s Python row shows the version and where it came from.
When no version is set and requirements.txt pins a release with no Linux wheels for the default line, Ingot builds on the newest line those pins have wheels for, and the build log names the pin that chose it. See Set the language version.
Build with a Dockerfile
Every Python guide ends its deployment with a Deploy with a Dockerfile section: Django, FastAPI, Flask, Streamlit, Gradio, Wagtail, Litestar and Dash. Their Dockerfiles build onpython:3.14-slim-trixie and do by hand what Ingot does for you:
- Proxy headers. Set
FORWARDED_ALLOW_IPS="*", so Gunicorn and Uvicorn trust theX-Forwarded-Protoheader from Sevalla’s proxy. Without it, a Django admin sign-in over HTTPS fails withCSRF verification failed. Request aborted.SetPYTHONUNBUFFERED=1too, so logs reach the runtime logs as they’re written. - Gunicorn settings. Ingot’s Gunicorn runs each worker with 4 threads, gives requests 20 seconds to finish after
SIGTERM, and closes each connection after its response. A Dockerfile passes the same as--threads 4 --graceful-timeout 20 --keep-alive 0: without--keep-alive 0, Gunicorn 26.2 waits out the whole graceful timeout before it stops whenever Sevalla’s proxy holds an idle connection open. - Commands for jobs and workers. Sevalla runs a process’s custom start command in a login shell,
/bin/sh -l -c, and keeps the image’sPATHin it. Commands installed with pip into the image’s Python, or into a uv virtual environment whosebinfolder the Dockerfile puts onPATH, are found the same way as in the image’sCMD. - Static files. For Django, run
collectstaticduring the build with a placeholderSECRET_KEY, so the real key never enters the image. - User. Run as user
1000with a home directory, where Gunicorn keeps its control socket, and leave the code owned by root, so the application can’t change it.
Connection variables
When you add an internal connection and turn on Add environment variables to the application, Sevalla adds these variables (see Environment variables an internal connection adds). You can rename them in the same sheet before you save, for exampleDB_URL to DATABASE_URL.
Two schemes need care in Python:
- SQLAlchemy (and so SQLModel, Flask-SQLAlchemy-Lite and Alembic) doesn’t accept
postgres://. Replace it withpostgresql+psycopg://in code. Django’s database URL parsers accept it as is. - redis-py, Celery and Django’s Redis cache accept
redis://,rediss://andunix://only. A ValkeyREDIS_URLadded before Oct 30, 2026 starts withvalkey://: replace it withredis://.