Skip to content

Deployment Guide

Deploy AvailEngine to production. This guide covers the full stack: FastAPI backend, React dashboard, and VitePress docs.

Architecture

┌─────────────────────────────────────────────┐
│                  Internet                     │
│  api.availengine.com → FastAPI (port 8000)   │
│  docs.availengine.com → VitePress static     │
└─────────────────────────────────────────────┘

         ├── Supabase (Postgres + Auth)
         ├── Stripe (payments)
         ├── Redis (rate limiting)
         ├── Resend (email)
         └── Brevo (transactional email)

The FastAPI server also serves the React dashboard SPA at /. In production, you can either:

  • Serve everything from one domain (API + dashboard at api.availengine.com)
  • Split API (api.availengine.com) and docs (docs.availengine.com)

Prerequisites

  • Supabase project (free tier works for development)
  • Stripe account (test mode during development)
  • Redis instance (Upstash, Redis Cloud, or self-hosted)
  • Resend API key (for email notifications)
  • Domain name (for production)

Environment Variables

Copy backend/.env.example to backend/.env and fill in every variable:

bash
# ── Environment ──────────────────────────────────────────
ENVIRONMENT=production                    # production | development
APP_BASE_URL=https://api.availengine.com
API_BASE_URL=https://api.availengine.com/v1
CORS_ORIGINS=["https://app.yourapp.com"]

# ── Supabase ─────────────────────────────────────────────
SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi...
SUPABASE_ANON_KEY=eyJhbGciOi...
DATABASE_URL=postgresql://postgres:...@db.xxxxx.supabase.co:5432/postgres

# ── Stripe ───────────────────────────────────────────────
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PRICE_STARTER=price_...
STRIPE_PRICE_GROWTH=price_...
STRIPE_PRICE_SCALE=price_...

# ── Redis ────────────────────────────────────────────────
REDIS_URL=redis://default:password@host:port

# ── Email ────────────────────────────────────────────────
RESEND_API_KEY=re_...
FROM_EMAIL=bookings@availengine.com
BREVO_API_KEY=xkeysib-...
BREVO_SENDER_EMAIL=bookings@availengine.com

# ── Google Calendar (optional) ───────────────────────────
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GOOGLE_REDIRECT_URI=https://api.availengine.com/integrations/google/callback
CALENDAR_ENCRYPTION_KEY=...

Dockerfile

dockerfile
# Dockerfile
FROM python:3.12-slim

WORKDIR /app

# Install Node.js for dashboard build
RUN apt-get update && apt-get install -y curl && \
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
    apt-get install -y nodejs && \
    apt-get clean

# Python dependencies
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Build dashboard
COPY dashboard/ dashboard/
RUN cd dashboard && npm install && npm run build

# Copy backend
COPY backend/ backend/
COPY docs/ docs/

EXPOSE 8000

CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose.yml

yaml
version: '3.8'
services:
  api:
    build: .
    ports:
      - "8000:8000"
    env_file:
      - backend/.env
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    restart: unless-stopped
    volumes:
      - redis_data:/data

volumes:
  redis_data:

Deploy

bash
docker-compose up -d

The API is live at http://localhost:8000. The dashboard is served at /. Swagger docs at /docs.

Option 2: Render

  1. Create a Web Service on Render
  2. Connect your GitHub repo
  3. Build Command: cd dashboard && npm install && npm run build && cd ..
  4. Start Command: uvicorn backend.main:app --host 0.0.0.0 --port $PORT
  5. Add all environment variables from the list above
  6. Add a Redis service (Render has managed Redis)
  7. Deploy

Option 3: Railway

  1. Create a new project on Railway
  2. Add your repo
  3. Add a Redis plugin
  4. Add environment variables
  5. Railway auto-detects the Python project and builds

Option 4: DigitalOcean App Platform

  1. Create an App on DigitalOcean
  2. Connect your repo
  3. Set build command: cd dashboard && npm install && npm run build
  4. Set run command: uvicorn backend.main:app --host 0.0.0.0 --port $PORT
  5. Add a managed Redis database
  6. Add environment variables

Docs Deployment

The VitePress docs are static HTML. Deploy to any static host:

Vercel

bash
cd docs
npm install
npx vitepress build
# Deploy docs/.vitepress/dist to Vercel

Set docs.availengine.com as the custom domain.

Netlify

  1. Connect repo to Netlify
  2. Base directory: docs
  3. Build command: npm install && npx vitepress build
  4. Publish directory: docs/.vitepress/dist

Domain & SSL

DNS Records

api.availengine.com    A     <server-ip>
docs.availengine.com   CNAME <vercel/netlify-url>

SSL

  • Render/Railway/DigitalOcean: SSL is auto-provisioned
  • Self-hosted: Use Caddy or nginx + certbot
  • Vercel/Netlify: SSL is automatic

Caddy (Self-Hosted)

api.availengine.com {
    reverse_proxy localhost:8000
}

Caddy auto-provisions and renews Let's Encrypt certificates.

Stripe Webhook Setup

After deploying, configure Stripe webhooks:

  1. Go to Stripe Dashboard > Webhooks
  2. Endpoint URL: https://api.availengine.com/webhooks/stripe
  3. Select events (see Stripe Setup for the full list)
  4. Copy the signing secret → set as STRIPE_WEBHOOK_SECRET env var

Verify Webhooks Work

bash
curl https://api.availengine.com/health
# → {"status": "ok"}

curl https://api.availengine.com/status
# → {"status": "ok", "version": "2.0.0", "components": {"database": "healthy", "stripe": "healthy"}}

Post-Deployment Checklist

  • [ ] All environment variables set (no defaults in production)
  • [ ] ENVIRONMENT=production set
  • [ ] Stripe webhooks configured and verified
  • [ ] Stripe products/prices created (Starter, Growth, Scale)
  • [ ] CORS origins set to your frontend domain(s)
  • [ ] API key prefix avail_live_ verified in the database
  • [ ] Rate limiting tested (default: 60 RPM per key)
  • [ ] Idempotency keys working (send duplicate POST with same key)
  • [ ] Billing enforcement active (test with past-due simulation)
  • [ ] Usage tracking writing to api_usage table
  • [ ] SSL certificate active and auto-renewing
  • [ ] DNS propagated (verify with curl -I https://api.availengine.com/health)
  • [ ] Supabase RLS policies verified (test with a scoped API key)
  • [ ] Email delivery working (test booking confirmation email)
  • [ ] Google Calendar OAuth configured (if using)
  • [ ] Docs site deployed and accessible
  • [ ] Dashboard SPA loads at /

Scaling

Vertical Scaling

Increase server resources. A 2 GB RAM / 2 vCPU instance handles ~500 concurrent bookings comfortably.

Horizontal Scaling

Run multiple API instances behind a load balancer. Ensure:

  • Redis is external (not per-instance) — rate limiting and idempotency use it
  • Stripe webhooks go to a single endpoint (or use a queue)
  • Supabase handles connection pooling automatically

Database

  • Supabase free tier: 500 MB database, good for development and early production
  • Supabase Pro ($25/month): 8 GB, daily backups, point-in-time recovery
  • Connection pooling: PgBouncer is built into Supabase — use port 6543 for pooled connections

Redis

  • Upstash free tier: 256 MB, good for rate limiting
  • Redis Cloud free tier: 30 MB
  • For 10k+ RPM, 256 MB is sufficient

Monitoring

Health Endpoints

  • GET /health — simple liveness check
  • GET /status — component health (DB, Stripe)

Logging

Structured JSON logs are written to stdout. In production, ship them to your logging platform:

  • Render: Logs appear in the dashboard
  • Railway: Built-in log viewer
  • Datadog/New Relic: Use their Python agent
  • Self-hosted: Pipe to journald or a log file

Alerting

Set up alerts for:

  • /status returning degraded (DB or Stripe down)
  • 5xx error rate spike
  • Stripe webhook failures (check Stripe Dashboard > Webhooks > Events)
  • Redis connection failures (rate limiting stops working)

Troubleshooting

500 Errors

  1. Check logs: docker-compose logs api
  2. Verify all env vars are set
  3. Check Supabase connection: curl https://xxxxx.supabase.co/rest/v1/ with apikey header
  4. Check Redis connection: redis-cli -u $REDIS_URL ping

Stripe Webhooks Failing

  1. Go to Stripe Dashboard > Webhooks > Events
  2. Check the response code and body
  3. Verify STRIPE_WEBHOOK_SECRET matches the dashboard signing secret
  4. Verify the endpoint URL is reachable from the internet

CORS Errors

  1. Check CORS_ORIGINS env var — it's a JSON array of allowed origins
  2. Must include the protocol: ["https://app.yourapp.com"]
  3. Wildcards (*) don't work with credentials

Rate Limiting Not Working

  1. Verify REDIS_URL is set and Redis is reachable
  2. Check that rate_limit_rpm is set on API keys (default: 60)
  3. The Retry-After header in 429 responses tells you how long to wait

Next: Stripe Setup → | Back to Getting Started →

Released under the MIT License.