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:
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
- Azure CLI installed and logged in
- Docker for container builds
- PostgreSQL client for database setup
- 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 aboveAZURE_REGISTRY_USERNAME
- ACR usernameAZURE_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 |