Skip to content

Deployment & Production

This guide covers deploying FastSvelte to production environments, with a focus on Azure Container Apps and other cloud platforms. FastSvelte is designed to be containerized and cloud-ready.

Overview

FastSvelte supports multiple deployment strategies:

  • Azure Container Apps - Recommended for full-scale production
  • Docker Compose - Development and small deployments
  • Traditional VPS - Custom server deployments
  • Vercel/Netlify - Frontend-only deployments with external API

Azure Container Apps Deployment

Azure Container Apps provides serverless containers with built-in scaling, load balancing, and managed infrastructure.

Architecture Overview

Internet → Azure Front Door → Container Apps → PostgreSQL Flexible Server
                         Static Web Apps (Frontend)
                         Blob Storage (File uploads)

Azure Resources Naming Convention

FastSvelte uses this naming format:

[appname]-[env]-[servicetype]-[regioncode]

Examples: - fs-prod-api-ca (FastSvelte Production API in Canada Central) - fs-beta-app-ca (FastSvelte Beta Frontend in Canada Central) - fs-dev-db-ca (FastSvelte Development Database in Canada Central)

Prerequisites

  1. Azure CLI installed and logged in
  2. Docker for container builds
  3. PostgreSQL client for database setup
  4. Domain name configured for custom domains

Step 1: Create Resource Group

# Create resource group
az group create \
  --name fs-prod-rg \
  --location canadacentral

# List available regions
az account list-locations --output table

Step 2: PostgreSQL Database

# Create PostgreSQL Flexible Server
az postgres flexible-server create \
  --resource-group fs-prod-rg \
  --name fs-prod-db-ca \
  --location canadacentral \
  --admin-user fsadmin \
  --admin-password <your-secure-password> \
  --sku-name Standard_B1ms \
  --storage-size 32 \
  --version 14

# Configure firewall for Azure services
az postgres flexible-server firewall-rule create \
  --resource-group fs-prod-rg \
  --name fs-prod-db-ca \
  --rule-name AllowAzureServices \
  --start-ip-address 0.0.0.0 \
  --end-ip-address 0.0.0.0

# Create application database
az postgres flexible-server db create \
  --resource-group fs-prod-rg \
  --server-name fs-prod-db-ca \
  --database-name fastsvelte

Database Configuration

Update PostgreSQL settings for production:

-- Connect to your database and run these commands
ALTER SYSTEM SET max_connections = 200;
ALTER SYSTEM SET shared_buffers = '256MB';
ALTER SYSTEM SET effective_cache_size = '1GB';
ALTER SYSTEM SET work_mem = '4MB';
ALTER SYSTEM SET maintenance_work_mem = '64MB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
ALTER SYSTEM SET wal_buffers = '16MB';
ALTER SYSTEM SET default_statistics_target = 100;

-- Reload configuration
SELECT pg_reload_conf();

Step 3: Deploy Database Schema

# Clone your repository
git clone <your-fastsvelte-repo>
cd fastsvelte/db

# Set up environment variables
cat > .env << EOF
DATABASE_URL_PROD="postgres://fsadmin:<password>@fs-prod-db-ca.postgres.database.azure.com/fastsvelte?sslmode=require"
EOF

# Deploy migrations
./sqitch.sh prod deploy

Step 4: Create Admin User

cd ../backend

# Create production admin
python scripts/create_admin.py \
  --env prod \
  --password <secure-admin-password> \
  --domain yourdomain.com

Step 5: Container Registry

# Create Azure Container Registry
az acr create \
  --resource-group fs-prod-rg \
  --name fsprodregistry \
  --sku Basic \
  --location canadacentral

# Enable admin access
az acr update \
  --name fsprodregistry \
  --admin-enabled true

# Get login credentials
az acr credential show --name fsprodregistry

Step 6: Build and Push Backend Container

# Login to registry
az acr login --name fsprodregistry

# Build and push backend image
cd backend
docker build -t fsprodregistry.azurecr.io/fastsvelte-api:latest .
docker push fsprodregistry.azurecr.io/fastsvelte-api:latest

Step 7: Container App Environment

# Create Container Apps Environment
az containerapp env create \
  --resource-group fs-prod-rg \
  --name fs-prod-env-ca \
  --location canadacentral

# Create Log Analytics Workspace (optional but recommended)
az monitor log-analytics workspace create \
  --resource-group fs-prod-rg \
  --workspace-name fs-prod-logs-ca \
  --location canadacentral

Step 8: Deploy Backend API

# Create Container App for backend
az containerapp create \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --environment fs-prod-env-ca \
  --image fsprodregistry.azurecr.io/fastsvelte-api:latest \
  --target-port 3100 \
  --ingress external \
  --min-replicas 1 \
  --max-replicas 10 \
  --cpu 1.0 \
  --memory 2Gi \
  --registry-server fsprodregistry.azurecr.io \
  --registry-username <registry-username> \
  --registry-password <registry-password>

Step 9: Configure Environment Variables

# Set environment variables for the API
az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --set-env-vars \
    FS_ENVIRONMENT=prod \
    FS_DB_URL="postgres://fsadmin:<password>@fs-prod-db-ca.postgres.database.azure.com/fastsvelte?sslmode=require" \
    FS_JWT_SECRET_KEY="<your-jwt-secret>" \
    FS_BASE_API_URL="https://fs-prod-api-ca.redocean-12345678.canadacentral.azurecontainerapps.io" \
    FS_BASE_WEB_URL="https://app.yourdomain.com" \
    FS_CORS_ORIGINS="https://app.yourdomain.com" \
    FS_EMAIL_PROVIDER="azure" \
    FS_SENDGRID_API_KEY="<your-sendgrid-key>" \
    FS_STRIPE_API_KEY="<your-stripe-key>" \
    FS_STRIPE_WEBHOOK_SECRET="<your-webhook-secret>"

Step 10: Static Web App for Frontend

# Create Static Web App
az staticwebapp create \
  --resource-group fs-prod-rg \
  --name fs-prod-app \
  --location canadacentral \
  --source https://github.com/yourusername/fastsvelte \
  --branch main \
  --app-location "frontend" \
  --build-location "frontend/build" \
  --login-with-github

Step 11: Custom Domains

# Add custom domain to Container App (API)
az containerapp hostname bind \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --hostname api.yourdomain.com

# Add custom domain to Static Web App (Frontend)
az staticwebapp hostname set \
  --resource-group fs-prod-rg \
  --name fs-prod-app \
  --hostname app.yourdomain.com

Azure Communication Services

For email functionality, set up Azure Communication Services:

Email Service

# Create Email Communication Service
az communication email create \
  --resource-group fs-prod-rg \
  --name fs-prod-email \
  --data-location "Canada"

# Get connection string
az communication show \
  --resource-group fs-prod-rg \
  --name fs-prod-email \
  --query "connectionString" \
  --output tsv

Communication Service

# Create Communication Service  
az communication create \
  --resource-group fs-prod-rg \
  --name fs-prod-comms-ca \
  --data-location "Canada"

# Get connection string
az communication list-key \
  --resource-group fs-prod-rg \
  --name fs-prod-comms-ca

Update your Container App environment variables:

az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --set-env-vars \
    FS_AZURE_EMAIL_CONNECTION_STRING="<connection-string>" \
    FS_AZURE_EMAIL_SENDER_ADDRESS="noreply@yourdomain.com"

GitHub Actions CI/CD

FastSvelte includes GitHub Actions workflows for automated deployment.

Setup Azure Credentials

# Create service principal for GitHub Actions
az ad sp create-for-rbac \
  --name fs-prod-deploy \
  --role contributor \
  --scopes /subscriptions/<subscription-id>/resourceGroups/fs-prod-rg \
  --json-auth \
  --output json

Add these secrets to your GitHub repository:

  • AZURE_CREDENTIALS - Output from the command above
  • AZURE_REGISTRY_USERNAME - ACR username
  • AZURE_REGISTRY_PASSWORD - ACR password

Backend Deployment Workflow

# .github/workflows/deploy-backend-prod.yml
name: Deploy Backend to Production

on:
  push:
    branches: [main]
    paths: ['backend/**']

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Login to Azure
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Login to ACR
        run: |
          az acr login --name fsprodregistry

      - name: Build and push Docker image
        run: |
          cd backend
          docker build -t fsprodregistry.azurecr.io/fastsvelte-api:${{ github.sha }} .
          docker push fsprodregistry.azurecr.io/fastsvelte-api:${{ github.sha }}

      - name: Deploy to Container App
        run: |
          az containerapp update \
            --resource-group fs-prod-rg \
            --name fs-prod-api-ca \
            --image fsprodregistry.azurecr.io/fastsvelte-api:${{ github.sha }}

Frontend Deployment

GitHub Actions automatically deploys Static Web Apps when connected to your repository.

Environment Management

FastSvelte supports multiple environments:

Environment Configuration

# Development
FS_ENVIRONMENT=dev
FS_BASE_API_URL=http://localhost:8000
FS_BASE_WEB_URL=http://localhost:5173

# Beta/Staging  
FS_ENVIRONMENT=beta
FS_BASE_API_URL=https://api-beta.yourdomain.com
FS_BASE_WEB_URL=https://app-beta.yourdomain.com

# Production
FS_ENVIRONMENT=prod
FS_BASE_API_URL=https://api.yourdomain.com
FS_BASE_WEB_URL=https://app.yourdomain.com

Multi-Environment Deployment

Create separate resource groups for each environment:

# Beta environment
az group create --name fs-beta-rg --location canadacentral

# Production environment  
az group create --name fs-prod-rg --location canadacentral

Monitoring and Logging

Log Analytics

Query Container App logs:

// Latest API errors
ContainerAppConsoleLogs_CL
| where ContainerAppName_s == 'fs-prod-api-ca'
| where LogLevel_s in ('ERROR', 'WARNING')
| order by TimeGenerated desc
| limit 100

// API response times
ContainerAppConsoleLogs_CL  
| where ContainerAppName_s == 'fs-prod-api-ca'
| where Message_s contains "response_time"
| extend ResponseTime = todouble(extract(@"response_time:(\d+\.?\d*)", 1, Message_s))
| summarize avg(ResponseTime) by bin(TimeGenerated, 5m)

// Database connection errors
ContainerAppConsoleLogs_CL
| where ContainerAppName_s == 'fs-prod-api-ca' 
| where Message_s contains "database" and LogLevel_s == 'ERROR'
| order by TimeGenerated desc

Application Insights

# Create Application Insights
az monitor app-insights component create \
  --resource-group fs-prod-rg \
  --app fs-prod-insights \
  --location canadacentral \
  --kind web

# Get instrumentation key
az monitor app-insights component show \
  --resource-group fs-prod-rg \
  --app fs-prod-insights \
  --query "instrumentationKey" \
  --output tsv

Add to Container App environment variables:

az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --set-env-vars \
    FS_APPINSIGHTS_CONNECTION_STRING="<connection-string>"

Security Best Practices

Network Security

# Restrict Container App ingress to specific IPs (optional)
az containerapp ingress access-restriction set \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --rule-name "AllowCloudflare" \
  --ip-address-range "173.245.48.0/20" \
  --description "Cloudflare IP range"

Key Vault Integration

# Create Key Vault
az keyvault create \
  --resource-group fs-prod-rg \
  --name fs-prod-keyvault \
  --location canadacentral

# Store secrets
az keyvault secret set \
  --vault-name fs-prod-keyvault \
  --name "jwt-secret" \
  --value "<your-jwt-secret>"

az keyvault secret set \
  --vault-name fs-prod-keyvault \
  --name "db-password" \
  --value "<your-db-password>"

Reference secrets in Container App:

az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --set-env-vars \
    FS_JWT_SECRET_KEY="secretref:jwt-secret"

SSL/TLS Configuration

Container Apps automatically provide SSL certificates for custom domains. Ensure HTTPS redirect:

# Add to FastAPI middleware
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware

if settings.environment == "prod":
    app.add_middleware(HTTPSRedirectMiddleware)

Scaling Configuration

Auto-scaling Rules

# Configure CPU-based scaling
az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --min-replicas 2 \
  --max-replicas 20 \
  --scale-rule-name "cpu-scale" \
  --scale-rule-type "cpu" \
  --scale-rule-metadata "type=Utilization" "value=70"

# Configure HTTP request scaling
az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --scale-rule-name "http-scale" \
  --scale-rule-type "http" \
  --scale-rule-metadata "concurrentRequests=100"

Database Scaling

# Scale PostgreSQL compute
az postgres flexible-server update \
  --resource-group fs-prod-rg \
  --name fs-prod-db-ca \
  --sku-name Standard_D2s_v3

# Scale storage
az postgres flexible-server update \
  --resource-group fs-prod-rg \
  --name fs-prod-db-ca \
  --storage-size 128

Backup and Disaster Recovery

Database Backups

# Enable automated backups (enabled by default)
az postgres flexible-server update \
  --resource-group fs-prod-rg \
  --name fs-prod-db-ca \
  --backup-retention 30

# Manual backup
az postgres flexible-server backup create \
  --resource-group fs-prod-rg \
  --name fs-prod-db-ca \
  --backup-name manual-backup-$(date +%Y%m%d)

Container Image Backups

Container images are automatically stored in Azure Container Registry with versioning.

Configuration Backup

Store infrastructure as code using Azure Resource Manager (ARM) templates or Bicep.

Alternative Deployment Options

Docker Compose Production

For smaller deployments, use Docker Compose with production configuration:

# docker-compose.prod.yml
version: '3.8'
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: fastsvelte
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  api:
    build:
      context: ./backend
      dockerfile: Dockerfile.prod
    environment:
      FS_ENVIRONMENT: prod
      FS_DB_URL: postgres://postgres@db:5432/fastsvelte
    secrets:
      - jwt_secret
      - stripe_key
    depends_on:
      - db
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.prod
    environment:
      PUBLIC_API_BASE_URL: https://api.yourdomain.com
    ports:
      - "443:443"
      - "80:80"
    restart: unless-stopped

secrets:
  db_password:
    external: true
  jwt_secret:
    external: true
  stripe_key:
    external: true

volumes:
  postgres_data:

Traditional VPS Deployment

For VPS deployment:

# Install dependencies
sudo apt update
sudo apt install -y docker.io docker-compose nginx certbot python3-certbot-nginx

# Clone and deploy
git clone <your-repo>
cd fastsvelte

# Setup environment
cp .env.example .env.prod
# Edit .env.prod with production values

# Deploy
docker-compose -f docker-compose.prod.yml up -d

# Setup SSL with Let's Encrypt
sudo certbot --nginx -d api.yourdomain.com -d app.yourdomain.com

Performance Optimization

CDN Configuration

Use Azure Front Door for global content delivery:

# Create Front Door profile
az afd profile create \
  --resource-group fs-prod-rg \
  --profile-name fs-prod-cdn \
  --sku Premium_AzureFrontDoor

# Add endpoints and routes
az afd endpoint create \
  --resource-group fs-prod-rg \
  --profile-name fs-prod-cdn \
  --endpoint-name api \
  --enabled-state Enabled

Database Performance

-- Create performance indexes
CREATE INDEX CONCURRENTLY idx_user_organization_active 
ON fastsvelte."user"(organization_id) WHERE is_active = true;

CREATE INDEX CONCURRENTLY idx_session_user_expires 
ON fastsvelte.session(user_id, expires_at);

CREATE INDEX CONCURRENTLY idx_event_log_org_created 
ON fastsvelte.event_log(organization_id, created_at);

Caching Strategy

Implement Redis for session caching:

# Create Azure Cache for Redis
az redis create \
  --resource-group fs-prod-rg \
  --name fs-prod-cache \
  --location canadacentral \
  --sku Basic \
  --vm-size c0

Maintenance and Updates

Rolling Updates

Container Apps support zero-downtime deployments:

# Deploy new version with revision suffix
az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --image fsprodregistry.azurecr.io/fastsvelte-api:v2.0.0 \
  --revision-suffix v2-0-0

# Traffic splitting for gradual rollout
az containerapp revision set-mode \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --mode multiple

az containerapp ingress traffic set \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --revision-weight "fs-prod-api-ca--v2-0-0=20" "fs-prod-api-ca--v1-0-0=80"

Database Migrations

# Connect to production and run migrations
cd db
./sqitch.sh prod deploy

# Verify deployment
./sqitch.sh prod status
./sqitch.sh prod verify

Health Checks

Container Apps automatically perform health checks. Configure custom health endpoints:

# Add to FastAPI app
@app.get("/health")
async def health_check():
    return {"status": "healthy", "timestamp": datetime.utcnow()}

Troubleshooting

Common Issues

Container App won't start:

# Check logs
az containerapp logs show \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --follow

Database connection issues:

# Test connection from Container App
az containerapp exec \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --command "psql $FS_DB_URL -c 'SELECT 1;'"

SSL certificate problems:

# Check certificate status
az containerapp hostname list \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca

Performance Issues

Monitor resource usage:

# Container App metrics
az monitor metrics list \
  --resource "/subscriptions/<sub-id>/resourceGroups/fs-prod-rg/providers/Microsoft.App/containerApps/fs-prod-api-ca" \
  --metric "CpuPercentage,MemoryPercentage,Requests" \
  --start-time 2024-01-01T00:00:00Z \
  --end-time 2024-01-01T23:59:59Z

Cost Optimization

Resource Sizing

Start with minimal resources and scale as needed:

# Optimize Container App resources
az containerapp update \
  --resource-group fs-prod-rg \
  --name fs-prod-api-ca \
  --cpu 0.5 \
  --memory 1Gi \
  --min-replicas 1 \
  --max-replicas 5

Reserved Capacity

For predictable workloads, use Azure Reserved Instances for cost savings.

Monitoring Costs

Set up cost alerts:

# Create budget alert
az consumption budget create \
  --resource-group fs-prod-rg \
  --budget-name fs-prod-budget \
  --amount 100 \
  --time-grain Monthly \
  --category Cost

Next Steps

After successful deployment, your FastSvelte application should be running and accessible to users.

Quick Reference

Essential Commands

# Deploy latest changes
az containerapp update --resource-group fs-prod-rg --name fs-prod-api-ca --image fsprodregistry.azurecr.io/fastsvelte-api:latest

# View logs
az containerapp logs show --resource-group fs-prod-rg --name fs-prod-api-ca --follow

# Scale manually  
az containerapp update --resource-group fs-prod-rg --name fs-prod-api-ca --min-replicas 2 --max-replicas 10

# Check health
curl https://api.yourdomain.com/health

Environment URLs

Environment API URL App URL
Development http://localhost:8000 http://localhost:5173
Beta https://api-beta.yourdomain.com https://app-beta.yourdomain.com
Production https://api.yourdomain.com https://app.yourdomain.com