Skip to content

Deploy Backend ​

This guide covers deploying the FastAPI backend to production.

Quick Deploy to Railway ​

Railway is the easiest platform for deploying Python applications with a generous free tier.

1. Prerequisites ​

  • GitHub account
  • Railway account (railway.app)
  • Code pushed to GitHub repository

2. Create New Project ​

  1. Go to railway.app
  2. Click "New Project"
  3. Select "Deploy from GitHub repo"
  4. Choose your acodeaday repository
  5. Railway auto-detects Python and starts deployment

3. Configure Root Directory ​

If your backend is in a subdirectory:

  1. Go to project Settings
  2. Set Root Directory: backend
  3. Redeploy

4. Add Environment Variables ​

Go to project Variables tab and add:

bash
DATABASE_URL=postgresql+asyncpg://postgres:PASSWORD@HOST:5432/postgres
SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_KEY=YOUR_ANON_KEY
JUDGE0_URL=https://your-judge0-instance.com
AUTH_USER_EMAIL=admin
AUTH_PASSWORD=your-secure-password
ENVIRONMENT=production
PYTHONUNBUFFERED=1

5. Configure Build Command ​

Railway auto-detects uvicorn, but you can customize:

  1. Go to Settings > Deploy
  2. Build Command: pip install uv && uv sync
  3. Start Command: uv run uvicorn app.main:app --host 0.0.0.0 --port $PORT

6. Run Migrations ​

After first deployment:

  1. Go to project Deployments
  2. Click on active deployment
  3. Open "Deploy Logs"
  4. You'll see the app URL

SSH into Railway (if needed):

bash
railway run uv run alembic upgrade head

Or run migrations locally against production DB:

bash
# Set DATABASE_URL to production
uv run alembic upgrade head

7. Seed Problems ​

bash
# Connect to production database
railway run uv run python scripts/seed_problems.py seed

Or locally:

bash
# Set DATABASE_URL to production
uv run python scripts/seed_problems.py seed

8. Get Deployment URL ​

Railway provides a URL like:

https://acodeaday-production.up.railway.app

Test it:

bash
curl https://your-app.up.railway.app/api/problems

Deploy to Fly.io ​

Fly.io offers more control and excellent performance.

1. Install Fly CLI ​

bash
# macOS
brew install flyctl

# Linux
curl -L https://fly.io/install.sh | sh

# Windows
powershell -Command "iwr https://fly.io/install.ps1 -useb | iex"

2. Login to Fly.io ​

bash
flyctl auth login

3. Create Dockerfile ​

Create backend/Dockerfile:

dockerfile
FROM python:3.12-slim

WORKDIR /app

# Install uv
RUN pip install uv

# Copy dependency files
COPY pyproject.toml uv.lock ./

# Install dependencies
RUN uv sync --frozen

# Copy application code
COPY . .

# Run migrations and start server
CMD uv run alembic upgrade head && uv run uvicorn app.main:app --host 0.0.0.0 --port 8080

4. Create fly.toml ​

bash
cd backend
flyctl launch

This creates fly.toml. Edit it:

toml
app = "acodeaday"
primary_region = "sjc"

[build]
  dockerfile = "Dockerfile"

[env]
  ENVIRONMENT = "production"
  PORT = "8080"

[[services]]
  internal_port = 8080
  protocol = "tcp"

  [[services.ports]]
    port = 80
    handlers = ["http"]

  [[services.ports]]
    port = 443
    handlers = ["tls", "http"]

[services.http_checks]
  interval = "10s"
  timeout = "2s"
  grace_period = "5s"
  path = "/health"

5. Set Secrets ​

bash
flyctl secrets set DATABASE_URL="postgresql+asyncpg://..."
flyctl secrets set SUPABASE_URL="https://..."
flyctl secrets set SUPABASE_KEY="..."
flyctl secrets set JUDGE0_URL="https://..."
flyctl secrets set AUTH_USER_EMAIL="admin"
flyctl secrets set AUTH_PASSWORD="your-password"

6. Deploy ​

bash
flyctl deploy

7. Run Migrations ​

bash
flyctl ssh console
uv run alembic upgrade head
exit

8. Get URL ​

bash
flyctl info

Your app will be at https://acodeaday.fly.dev

Deploy to Render ​

Render offers a free tier with automatic deploys from Git.

1. Create Web Service ​

  1. Go to render.com
  2. Click "New +" > "Web Service"
  3. Connect GitHub repository
  4. Configure:
    • Name: acodeaday-backend
    • Region: Choose closest
    • Branch: main
    • Root Directory: backend
    • Runtime: Python 3
    • Build Command: pip install uv && uv sync
    • Start Command: uv run uvicorn app.main:app --host 0.0.0.0 --port $PORT

2. Add Environment Variables ​

In Environment tab:

bash
DATABASE_URL=postgresql+asyncpg://...
SUPABASE_URL=https://...
SUPABASE_KEY=...
JUDGE0_URL=https://...
AUTH_USER_EMAIL=admin
AUTH_PASSWORD=your-password
ENVIRONMENT=production

3. Deploy ​

Click "Create Web Service". Render auto-deploys on every Git push.

4. Run Migrations ​

After first deploy, open Shell:

bash
uv run alembic upgrade head
uv run python scripts/seed_problems.py seed

Environment Variables Reference ​

Required for all deployment platforms:

bash
# Database
DATABASE_URL=postgresql+asyncpg://USER:PASSWORD@HOST:PORT/DATABASE

# Supabase
SUPABASE_URL=https://PROJECT_ID.supabase.co
SUPABASE_KEY=your_anon_key

# Judge0
JUDGE0_URL=https://your-judge0-instance.com
# Optional: JUDGE0_API_KEY=your_key (if using auth)

# Authentication
AUTH_USER_EMAIL=admin
AUTH_PASSWORD=your-secure-password

# Environment
ENVIRONMENT=production

# Optional
LOG_LEVEL=INFO
CORS_ORIGINS=https://your-frontend.com

Health Check Endpoint ​

Add health check to backend/app/main.py:

python
@app.get("/health")
async def health_check():
    return {"status": "healthy"}

Most platforms use this for uptime monitoring.

CORS Configuration ​

Update CORS for production in backend/app/main.py:

python
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "https://your-frontend.vercel.app",
        "https://yourdomain.com"
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Logging ​

Configure structured logging for production:

python
import structlog

structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.processors.JSONRenderer()
    ],
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    cache_logger_on_first_use=True,
)

Monitoring ​

Add Sentry ​

bash
uv add sentry-sdk[fastapi]
python
import sentry_sdk

sentry_sdk.init(
    dsn="your-sentry-dsn",
    environment="production",
    traces_sample_rate=1.0,
)

SSL/TLS ​

All platforms (Railway, Fly.io, Render) provide automatic HTTPS. No configuration needed!

Database Connection Pooling ​

For production, use connection pooling:

python
# backend/app/db.py
engine = create_async_engine(
    DATABASE_URL,
    pool_size=10,
    max_overflow=20,
    pool_pre_ping=True,
)

Performance Optimization ​

Enable Gzip Compression ​

python
from fastapi.middleware.gzip import GZipMiddleware

app.add_middleware(GZipMiddleware, minimum_size=1000)

Add Caching ​

Use Redis for caching:

bash
uv add redis
python
import redis.asyncio as redis

cache = redis.from_url("redis://localhost:6379")

Troubleshooting ​

Deployment fails ​

bash
# Check logs
railway logs  # Railway
flyctl logs   # Fly.io
# Render: View logs in dashboard

Database connection errors ​

  • Verify DATABASE_URL includes +asyncpg
  • Check Supabase allows connections from your platform
  • Ensure SSL mode is correct

Import errors ​

bash
# Ensure all deps in pyproject.toml
uv add missing-package
git commit && git push

Next Steps ​