TrustReply consists of two services:
- Backend -- FastAPI (Python 3.13) serving the API on port 8000
- Frontend -- Next.js 16 serving the UI on port 3000
Both have Dockerfiles ready to go. Pick either Railway (simplest) or Fly.io (more control).
| Item | Railway | Fly.io |
|---|---|---|
| Account | railway.com | fly.io |
| CLI | npm i -g @railway/cli |
brew install flyctl or docs |
| Database | Provision a Railway Postgres plugin, or use Supabase | Provision fly postgres create, or use Supabase |
You will also need a Supabase project if you want authentication and/or a managed Postgres database.
Set these on whichever platform you deploy to. All are prefixed QF_ for the backend.
| Variable | Required | Description |
|---|---|---|
QF_DATABASE_URL |
Yes | Async DB URL, e.g. postgresql+asyncpg://user:pass@host:5432/db |
QF_SUPABASE_URL |
For auth | https://xxxxx.supabase.co |
QF_SUPABASE_ANON_KEY |
For auth | Supabase anon/public key |
QF_SUPABASE_SERVICE_KEY |
For auth | Supabase service role key |
QF_SUPABASE_JWT_SECRET |
For auth | JWT secret from Supabase dashboard |
QF_SIMILARITY_THRESHOLD |
No | Default 0.75 |
QF_CORS_ORIGINS |
Yes | JSON array, e.g. ["https://trustreply-frontend.up.railway.app"] |
QF_AGENT_ENABLED |
No | true to enable LLM agent |
QF_AGENT_PROVIDER |
No | openai (default) |
QF_AGENT_API_BASE |
No | https://api.openai.com/v1 |
QF_AGENT_API_KEY |
If agent | Your OpenAI (or compatible) API key |
QF_AGENT_MODEL |
No | gpt-4.1-nano (default) |
QF_API_KEY |
No | Optional shared API key for simple auth |
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
Yes | URL of the deployed backend, e.g. https://trustreply-backend.up.railway.app |
NEXT_PUBLIC_SUPABASE_URL |
For auth | Same as backend QF_SUPABASE_URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
For auth | Same as backend QF_SUPABASE_ANON_KEY |
Railway deploys each service from its own directory. Each directory has a railway.toml that tells Railway to use the Dockerfile.
npm i -g @railway/cli
railway loginrailway initIn the Railway dashboard, click + New inside your project and add PostgreSQL. Copy the connection string and convert it to async format:
postgresql+asyncpg://user:pass@host:port/dbname
cd backend
railway upThen set environment variables in the Railway dashboard (Settings > Variables) for the backend service. At minimum set QF_DATABASE_URL and QF_CORS_ORIGINS.
cd frontend
railway upSet the frontend build variables in Railway dashboard. Make sure NEXT_PUBLIC_API_URL points to your backend's Railway URL.
railway run --service backend alembic upgrade headIn the Railway dashboard, go to each service's Settings > Networking and click Generate Domain (or attach a custom domain).
Fly.io uses fly.backend.toml and fly.frontend.toml at the project root.
# macOS
brew install flyctl
# or
curl -L https://fly.io/install.sh | sh
fly auth loginfly postgres create --name trustreply-dbSave the connection string. Convert to async format:
postgresql+asyncpg://user:pass@trustreply-db.flycast:5432/dbname
fly launch --config fly.backend.toml --no-deploySet secrets:
fly secrets set \
QF_DATABASE_URL="postgresql+asyncpg://..." \
QF_CORS_ORIGINS='["https://trustreply-frontend.fly.dev"]' \
QF_SUPABASE_URL="https://xxxxx.supabase.co" \
QF_SUPABASE_ANON_KEY="eyJ..." \
QF_SUPABASE_SERVICE_KEY="eyJ..." \
QF_SUPABASE_JWT_SECRET="your-jwt-secret" \
--app trustreply-backendDeploy:
fly deploy --config fly.backend.tomlfly launch --config fly.frontend.toml --no-deploySet build args (Fly uses --build-arg at deploy time):
fly deploy --config fly.frontend.toml \
--build-arg NEXT_PUBLIC_API_URL=https://trustreply-backend.fly.dev \
--build-arg NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co \
--build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...fly ssh console --app trustreply-backend -C "cd /app && alembic upgrade head"fly postgres attach trustreply-db --app trustreply-backendAfter every deploy that includes model changes, run migrations:
Railway:
railway run --service backend alembic upgrade headFly.io:
fly ssh console --app trustreply-backend -C "cd /app && alembic upgrade head"Docker Compose (local):
docker compose exec backend alembic upgrade headTo generate a new migration after changing models:
alembic revision --autogenerate -m "describe your change"- Check logs:
railway logsorfly logs --app trustreply-backend - Ensure
QF_DATABASE_URLis set and the database is reachable - The health endpoint is
GET /api/health-- it should return 200
- Verify
QF_CORS_ORIGINSon the backend includes the frontend's URL (withhttps://, no trailing slash) - Verify
NEXT_PUBLIC_API_URLon the frontend points to the backend's public URL - Both values must use the same protocol (
https://)
- Railway: make sure the Postgres plugin is in the same project, or that the Supabase URL is accessible
- Fly.io: make sure the Postgres app is running (
fly status --app trustreply-db) and attached - Check that the URL uses
postgresql+asyncpg://(notpostgres://)
- The backend Dockerfile downloads the
all-MiniLM-L6-v2model (~80 MB) at build time. This is intentional to avoid cold-start delays - Railway: if the build runs out of memory, upgrade to a plan with more build resources
- Fly.io: builder VMs have 8 GB RAM by default, which should be sufficient
Run alembic upgrade head to apply pending migrations. If you see conflicts, run alembic heads to check for multiple heads and merge them with alembic merge.