Deploy FastAPI Starter
FastAPI on Python 3.15 + uv: Postgres, Alembic, OTel, health probes
fastapi
Just deployed
Postgres
Just deployed
/var/lib/postgresql/data
Deploy and Host FastAPI
Deploy this FastAPI Starter on Railway and get a production-ready Python 3.15 API with PostgreSQL, Alembic migrations, health probes, and OpenTelemetry telemetry in one click.
About Hosting
Hosting FastAPI on Railway gives you a managed, usage-based platform for your Python API: automatic TLS, deploys from prebuilt images, vertical and horizontal scaling, built-in observability, and a bundled PostgreSQL 18 database (our hardened image with essential extensions) provisioned alongside your app. This starter runs on the latest stable Python (3.15) with a uv-locked dependency tree, runs database migrations before serving traffic, and reports liveness/readiness so Railway only routes traffic to healthy instances.
Why Deploy FastAPI Starter on Railway?
This template wires the full production path for you: a hardened multi-stage Docker image (non-root, slim), a bundled PostgreSQL 18 service from our own hardened image (persistent volume, extensions preloaded) with the connection string auto-composed over Railway's private network, additive Alembic migrations executed on boot, native OpenTelemetry traces/metrics/logs flowing into Railway's Observability tab, and JSON structured logging out of the box. Instead of wiring a framework version, database, migrations, health checks, and telemetry yourself, deploy once and start writing endpoints — the reference CRUD resource shows the pattern to copy.
What's included
| Service | Type | Purpose |
| --- | --- |
| fastapi | Web Service | The FastAPI app: async SQLModel, Alembic on boot, health probes |
| Postgres | Database | PostgreSQL 18 (our hardened image, persistent volume); connection URL auto-composed as DATABASE_URL |
Key features
- Latest stack: FastAPI 0.162.4 on Python 3.15.2, uv-locked for reproducible builds
- PostgreSQL + Alembic: bundled PostgreSQL 18 service (persistent volume, extensions preloaded), additive migrations run before serving
- Health probes:
/healthzliveness (Railway healthcheck) and/readyzDB readiness - Native OpenTelemetry: traces, metrics, and logs export to Railway's OTLP collector — no code needed
- Production hygiene: non-root multi-stage image, JSON logging,
WEB_CONCURRENCYworker scaling, graceful shutdown
Common Use Cases
- Backend developer launches a production FastAPI API with Postgres in one click
- Team standardizes on a modern, pinned Python API starter with observability built in
- Solo dev prototypes an API locally (uv + SQLite) and ships the same code to Railway
- Anyone needing a reference for async SQLModel, Alembic zero-downtime migrations, and health-probe patterns
Dependencies for FastAPI
Deployment Dependencies
| Name | Version | Purpose |
|---|---|---|
| Python | 3.15.2 | Runtime (latest stable) |
| FastAPI | 0.162.4 | Web framework (pinned) |
| uvicorn / fastapi-cli | locked in uv.lock | ASGI server via the official fastapi run CLI |
| SQLModel + SQLAlchemy async | locked | ORM over asyncpg |
| Alembic | locked | Versioned, additive migrations |
| PostgreSQL | 18 (our image) | Bundled database service with persistent volume |
Next steps
- Open the service domain and visit
/docs— the interactive Swagger UI with the reference CRUD at/api/v1/items - Create an item via
POST /api/v1/items, then list withGET /api/v1/items - Open the Observability tab and watch request spans arrive via OpenTelemetry
- Copy
app/db/models/item.py+app/api/routes/items.pyto add your own resources, then autogenerate a migration
🐍 Deep Dive — FastAPI Starter
📐 Architecture
graph TD
Client([Client / Frontend]) --> Gateway[Railway Public Domain + TLS]
Gateway --> Web[fastapi service<br>Python 3.15 · uvicorn workers]
Web --> PG[(Postgres service<br>auto-provisioned)]
Web --> OTel[Railway Observability<br>OTLP traces · metrics · logs]
⚙️ Environment Variables
Wired automatically (no input needed): DATABASE_URL is composed from the bundled Postgres service credentials — postgresql://${{Postgres.POSTGRES_USER}}:${{Postgres.POSTGRES_PASSWORD}}@${{Postgres.RAILWAY_PRIVATE_DOMAIN}}:5432/${{Postgres.POSTGRES_DB}} — and normalized to the asyncpg driver at runtime. Set DATABASE_URL explicitly to use an external database instead.
User-configurable:
| Variable | Default | Description |
|---|---|---|
PORT | 8000 | Injected by Railway; the entrypoint binds it automatically. |
WEB_CONCURRENCY | 2 | Uvicorn workers (read natively by the FastAPI CLI). Scale with CPU. |
ENVIRONMENT | production | Environment label (production, staging, …). |
LOG_LEVEL | info | debug · info · warning · error — logs ship as JSON. |
BACKEND_CORS_ORIGINS | (empty) | Comma-separated allowed CORS origins. |
RUN_MIGRATIONS | true | Run alembic upgrade head before serving. |
DB_POOL_SIZE / DB_MAX_OVERFLOW | 10 / 10 | Async connection pool tuning. |
FASTAPI_OTEL_AUTO_CONFIGURE | true | Native OTel export (active when Railway's OTLP endpoint is present). |
🚀 Local Development
Prerequisites: uv (installs Python 3.15 automatically).
git clone && cd fastapi
cp .env.example .env
uv sync --all-groups # resolves uv.lock exactly
uv run fastapi dev # hot-reload dev server on :8000
uv run pytest # test suite (async SQLite — no Postgres needed)
uv run alembic upgrade head # apply migrations locally
Handy commands:
uv run alembic revision --autogenerate -m "add orders table" # new migration
uv lock && uv sync --all-groups # after dependency changes
bash e2e/run-tests.sh # full template validation
Shipping to Railway
railway init # or link an existing project
railway up # builds from the Dockerfile (config-as-code in railway.toml)
🩺 Health & Observability
| Endpoint | Purpose | Behavior |
|---|---|---|
/healthz | Liveness (Railway healthcheckPath) | Cheap JSON 200 — never touches the DB. |
/readyz | Readiness | SELECT 1 round-trip; 503 when the DB is down. |
/docs, /redoc | Interactive API docs | Auto-generated OpenAPI. |
/api/v1/items | Reference CRUD | Copy this pattern for your own resources. |
🧱 Building Your Own API on Top
- New resource → copy the
app/db/models/item.pysplit (Base → table → Create/Update/Public), thenuv run alembic revision --autogenerate -m "…". - New endpoints → add a router under
app/api/routes/, include it inapp/main.py. - New config → add the field to
app/core/config.py; it becomes an env var automatically. - Migrations stay additive — nullable columns first, chunked backfills,
CREATE INDEX CONCURRENTLYon live tables (zero-downtime policy).
🧪 End-to-End (E2E) Testing
bash e2e/run-tests.sh
Validates structure, marketplace publish config, manifest wiring (Postgres → DATABASE_URL), uv.lock freshness, byte-compilation, pytest, Docker build, and a live container smoke probe of /healthz + /docs. CI runs hadolint + this suite on every push/PR.
Maintained by the template publisher. Deployments are eligible for Railway's standard template support flow — questions land in the publisher's Template Queue.
Template Content
fastapi
wotonews/fastapi:v1.1.0PORT
LOG_LEVEL
ENVIRONMENT
RUN_MIGRATIONS
WEB_CONCURRENCY
Postgres
wotonews/postgres:v18.6-1POSTGRES_DB
POSTGRES_USER
POSTGRES_PASSWORD