CatalyzeUpDocs
impact pulse / deployment

Azure Infrastructure

Azure Infrastructure

Overview

Impact Pulse is deployed on Microsoft Azure using a combination of managed services optimized for cost efficiency and scalability. The infrastructure is provisioned via Terraform and deployed through GitHub Actions CI/CD pipelines.

Resource Group

All resources live under a single resource group for simplified management.

Property Value
Name rg-catalyzeup
Region East US
Subscription CatalyzeUp

Core Resources

Cosmos DB (MongoDB API)

The primary database, using Azure Cosmos DB with the MongoDB API in serverless mode for cost optimization.

Property Value
Account Name cosmos-catalyzeup
API MongoDB
Capacity Mode Serverless
Database impactpulse
Consistency Session
Backup Continuous (7-day retention)

Collections:

  • users
  • organizations
  • organization_members
  • organization_invitations
  • surveys
  • questions
  • survey_sessions
  • survey_responses
  • maslow_scores
  • email_reminders

Why Serverless: For a pet project with unpredictable traffic, serverless billing (per-request) is significantly cheaper than provisioned throughput. Scales to zero when idle.

Redis Cache

Azure Cache for Redis, used for session caching, rate limiting, and Celery task queue.

Property Value
Name redis-catalyzeup
SKU Basic C0
Memory 250 MB
TLS Enabled
Port 6380 (TLS)

Use Cases:

  • JWT token blacklisting
  • Session data caching
  • Rate limiting counters
  • Celery broker for background tasks
  • Survey progress auto-save buffer

App Service (Backend API)

The FastAPI backend runs on Azure App Service.

Property Value
Name app-impactpulse-api
Runtime Python 3.11
Plan B1 (Basic)
OS Linux
Always On Yes
Custom Domain impactpulse-api.catalyzeupdev.com

Configuration:

  • Gunicorn with Uvicorn workers
  • Health check endpoint: /api/v1/health
  • Deployment slots: Production only (B1 tier)
  • Managed Identity for Cosmos DB and Redis access

Environment Variables:

MONGODB_URL=<cosmos-connection-string>
MONGODB_DATABASE=impactpulse
REDIS_URL=<redis-connection-string>
SECRET_KEY=<from-key-vault>
FRONTEND_URL=https://impactpulse.catalyzeupdev.com
ENVIRONMENT=production
DEBUG=false
RESEND_API_KEY=<from-key-vault>

Static Web App (Frontend)

The React + Vite frontend is deployed as an Azure Static Web App.

Property Value
Name swa-impactpulse
SKU Free
Framework React (Vite)
Custom Domain impactpulse.catalyzeupdev.com
API Backend Proxied to App Service

Build Configuration:

  • App location: frontend
  • Output location: dist
  • Build command: npm run build

Cloudflare DNS

DNS management and CDN/security are handled through Cloudflare.

Record Type Value Proxy
impactpulse.catalyzeupdev.com CNAME SWA default hostname Orange (proxied)
impactpulse-api.catalyzeupdev.com CNAME App Service hostname Orange (proxied)

Cloudflare Features:

  • SSL/TLS: Full (strict)
  • Always Use HTTPS: On
  • Minimum TLS Version: 1.2
  • DDoS protection (free tier)
  • CDN caching for static assets

CI/CD Pipeline

GitHub Actions

Both frontend and backend have automated deployment pipelines triggered on push to the main branch.

Backend Workflow (.github/workflows/deploy-backend.yml):

  1. Checkout code
  2. Set up Python 3.11
  3. Install dependencies
  4. Run tests
  5. Deploy to App Service via az webapp deploy

Frontend Workflow (.github/workflows/deploy-frontend.yml):

  1. Checkout code
  2. Set up Node.js
  3. Install dependencies
  4. Build (npm run build)
  5. Deploy to Static Web App via Azure/static-web-apps-deploy action

Deployment Strategy

  • Backend: Direct deployment to App Service (B1 does not support deployment slots)
  • Frontend: Atomic deployment via SWA (automatic rollback on failure)
  • Database: Schema changes applied through application startup (MongoDB is schemaless)

Security

Key Vault

Sensitive configuration is stored in Azure Key Vault.

Secret Description
cosmos-connection-string Cosmos DB MongoDB connection string
redis-connection-string Redis Cache connection string
jwt-secret-key JWT signing key
resend-api-key Resend email API key
google-oauth-client-id Google OAuth client ID
google-oauth-client-secret Google OAuth client secret

Network Security

  • Cosmos DB: IP firewall restricting access to App Service and developer IPs
  • Redis: Access only from App Service VNet (when upgraded)
  • App Service: HTTPS only, TLS 1.2 minimum
  • Static Web App: HTTPS only via Cloudflare

Cost Optimization

Current Monthly Estimate

Resource SKU Estimated Cost
Cosmos DB (serverless) Serverless ~$1-5/month (low traffic)
Redis Cache Basic C0 ~$16/month
App Service B1 Linux ~$13/month
Static Web App Free $0/month
Cloudflare Free $0/month
Total ~$30-34/month

Scaling Path

When traffic grows:

  1. Cosmos DB: Remains serverless until > 1M requests/month, then switch to provisioned autoscale
  2. Redis: Upgrade to Standard C1 for persistence and replication
  3. App Service: Scale up to S1/P1v3 for deployment slots and better performance
  4. Static Web App: Upgrade to Standard for custom auth, larger file sizes

Monitoring

Application Insights

  • Connected to App Service for backend monitoring
  • Request tracing and performance metrics
  • Exception tracking and alerting
  • Custom metrics for survey completions, reminder delivery

Health Checks

  • App Service: Built-in health check on /api/v1/health
  • Cosmos DB: Connection monitoring via Application Insights
  • Redis: Connection pool monitoring

Alerts

  • Backend 5xx error rate > 5%
  • Response time P95 > 2 seconds
  • Cosmos DB RU consumption > 80% (if provisioned)
  • Redis memory usage > 80%

Disaster Recovery

Backup Strategy

  • Cosmos DB: Continuous backup with 7-day point-in-time restore
  • Redis: No persistence on Basic tier (cache only, data reconstructed from DB)
  • App Service: Source code in GitHub (redeployable)
  • Static Web App: Source code in GitHub (redeployable)

Recovery Procedures

  1. Database corruption: Restore from Cosmos DB continuous backup
  2. Backend failure: Redeploy from GitHub Actions
  3. Frontend failure: Redeploy from GitHub Actions
  4. Redis failure: Restart cache (data is ephemeral, recreated by application)