Skip to content

Deployment

Backend

Environment Variables

# Production
DATABASE_URL=postgresql://user:pass@host:5432/saas_courier
SECRET_KEY=your-production-secret
ENVIRONMENT=production

# Optional
RESEND_API_KEY=your-resend-api-key
AERODATABOX_API_KEY=your-rapidapi-key

Running

# Build
uv run pip install -e .

# Run
uv run gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000

Frontend

Build

cd saas-fronted
uv run flet build web
# Output in ./build/web

Environment

# .env
API_BASE_URL=https://api.saas-courier.com
WS_BASE_URL=wss://api.saas-courier.com

Documentation (Cloudflare Workers)

Architecture

The documentation site uses Cloudflare Workers with KV storage for serving static files.

┌─────────────┐     ┌──────────────────┐     ┌─────────────┐
│  GitHub     │────▶│  GitHub Actions │────▶│ Cloudflare  │
│  Push       │     │  mkdocs build   │     │   Workers   │
└─────────────┘     │  wrangler deploy│     │  + KV Store│
                   └──────────────────┘     └─────────────┘

Requirements

Requirement Version Notes
Node.js >= 22.0.0 Wrangler 4.x requires Node.js 22+
Python 3.12 For mkdocs
Wrangler 4.x Installed via npm

GitHub Actions Workflow

Location: .github/workflows/deploy-docs.yml

Trigger paths:

on:
  push:
    branches: [main]
    paths:
      - 'docs/**'
      - 'mkdocs.yml'
      - 'wrangler.toml'
      - '.github/workflows/**'
      - 'workers-site/**'

⚠️ Important: The workers-site/** and .github/workflows/** paths must be included in the trigger configuration. Without these, workflow may not trigger on workers-site changes or workflow modifications.

Wrangler Configuration

File: wrangler.toml

name = "saas-courier-docs"
compatibility_date = "2026-05-15"
main = "workers-site/index.js"

compatibility_flags = ["nodejs_compat"]

[[kv_namespaces]]
binding = "ASSETS"
id = "af529f0e7da645da9d39ed407c5a11f2"

[site]
bucket = "./site"
Field Description
main Entry point for Cloudflare Workers
[site] bucket Static files output directory (from mkdocs build)
[kv_namespaces] KV namespace for asset caching

Workers Site

The workers-site/index.js file handles KV-based asset serving:

import { getAssetFromKV } from '@cloudflare/kv-asset-handler';

addEventListener('fetch', event => {
  event.respondWith(handleEvent(event));
});

async function handleEvent(event) {
  try {
    return await getAssetFromKV(event);
  } catch (e) {
    console.error('Error serving asset:', e);
    return new Response('Error loading page', { status: 500 });
  }
}

Deploy

# Preview (develop)
git push origin develop

# Production (main)
git push origin main

Custom Domain

saas-courier-docs.juanant0ni0.workers.dev

Troubleshooting

Wrangler requires Node.js v22.0.0

Error:

Wrangler requires at least Node.js v22.0.0. You are using v20.20.2.

Solution: Update Node.js version in GitHub Actions workflow:

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '22.12.0'

Build failed - Could not resolve "@cloudflare/kv-asset-handler"

Cause: Missing or incorrect workers-site configuration.

Solution: Ensure: 1. workers-site/index.js exists in repository 2. main = "workers-site/index.js" is in wrangler.toml 3. @cloudflare/kv-asset-handler is in package.json dependencies

Workflow not triggering

Cause: Missing paths in workflow trigger configuration.

Solution: Ensure trigger includes:

paths:
  - 'docs/**'
  - 'mkdocs.yml'
  - 'wrangler.toml'
  - '.github/workflows/**'
  - 'workers-site/**'

Workers Sites deprecated warning

Warning:

Because you've defined a [site] configuration, we're defaulting to "workers-site"
for the deprecated `site.entry-point` field.

This is expected. The workers-site approach is still functional.


Environment Reference

Variable Description
DATABASE_URL PostgreSQL connection string
SECRET_KEY JWT signing key (min 32 chars)
ENVIRONMENT development or production
CORS_ORIGINS Allowed origins (comma-separated)

Backup & Restore

PostgreSQL Backup

# Backup
docker-compose exec db pg_dump -U courieruser courierflow > backup.sql

# Restore
docker-compose exec -T db psql -U courieruser courierflow < backup.sql

Scaling

Horizontal Scaling

For multiple API instances:

  1. Use REDIS_URL for shared WebSocket state
  2. Ensure SECRET_KEY is identical across instances
  3. Use PostgreSQL as shared session store
  4. Load balancer with sticky sessions for WebSocket

Database Performance

-- Add indexes for common queries
CREATE INDEX idx_shipments_tenant_id ON shipments(tenant_id);
CREATE INDEX idx_shipments_tracking_id ON shipments(tracking_id);
CREATE INDEX idx_shipments_status ON shipments(status);
CREATE INDEX idx_sessions_user_id ON sessions(user_id);

Nginx Configuration

server {
    listen 443 ssl;
    server_name api.saas-courier.com;

    ssl_certificate /etc/ssl/certs/cert.pem;
    ssl_certificate_key /etc/ssl/private/key.pem;

    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /ws {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Ver también: Docker · Backup & Restore · Scaling