MicProtocol / docs /formbricks-runbook.md
Benaah's picture
Upload folder using huggingface_hub
b17baae verified
|
Raw History Blame Contribute Delete
16.1 kB

Formbricks v4 Operational Runbook

Overview

Formbricks v4 uses a 5-container microservice architecture for self-hosted deployments. This runbook covers day-to-day operations, troubleshooting, and disaster recovery for the MicProtocol deployment.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Formbricks v4 Stack                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚
β”‚  β”‚  formbricks │◄───►│formbricks-hub│◄───►│formbricks-cubeβ”‚          β”‚
β”‚  β”‚   (web)     β”‚     β”‚   (API)      β”‚     β”‚ (Analytics)  β”‚          β”‚
β”‚  β”‚   :3000     β”‚     β”‚   :8080      β”‚     β”‚   :4000      β”‚          β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜          β”‚
β”‚         β”‚                   β”‚                   β”‚                  β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                             β–Ό                                       β”‚
β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                             β”‚
β”‚                    β”‚  PostgreSQL     β”‚                             β”‚
β”‚                    β”‚  (formbricks DB)β”‚                             β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                             β”‚
β”‚                             β”‚                                       β”‚
β”‚                             β–Ό                                       β”‚
β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                             β”‚
β”‚                    β”‚     Redis       β”‚                             β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                             β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Container Details

Container Image Port Purpose Health Check
micprotocol-formbricks-migrate ghcr.io/formbricks/formbricks:latest - DB migrations (one-shot) N/A (completion)
micprotocol-formbricks ghcr.io/formbricks/formbricks:latest 3001 Main web application /api/health
micprotocol-formbricks-hub-migrate ghcr.io/formbricks/hub:latest - Hub DB migrations (one-shot) N/A (completion)
micprotocol-formbricks-hub ghcr.io/formbricks/hub:latest 8080 Hub API (survey delivery) /health
micprotocol-formbricks-cube cubejs/cube:v1.6.6 4000 Analytics engine (Cube.js) /readyz

Required Environment Variables

Critical Secrets (Generate Once, Never Rotate)

# Core Auth - NEVER ROTATE AFTER INITIAL SETUP
FORMBRICKS_NEXTAUTH_SECRET=$(openssl rand -base64 32)
FORMBRICKS_ENCRYPTION_KEY=$(openssl rand -hex 32)        # 64 chars
FORMBRICKS_HUB_API_KEY=$(openssl rand -hex 32)
FORMBRICKS_CUBE_API_SECRET=$(openssl rand -hex 32)

# Rotatable Secrets
FORMBRICKS_CRON_SECRET=$(openssl rand -base64 16)
FORMBRICKS_WEBHOOK_SECRET=$(openssl rand -hex 32)

Service Configuration

# URLs
FORMBRICKS_URL=https://formbricks.yourdomain.com
WEBAPP_URL=${FORMBRICKS_URL}
NEXTAUTH_URL=${FORMBRICKS_URL}

# Database
DATABASE_URL=postgresql://user:pass@postgres:5432/formbricks?schema=public

# Redis
REDIS_URL=redis://:password@redis:6379

# Inter-service Communication
HUB_API_URL=http://formbricks-hub:8080
HUB_API_KEY=${FORMBRICKS_HUB_API_KEY}
CUBEJS_API_URL=http://formbricks-cube:4000
CUBEJS_API_SECRET=${FORMBRICKS_CUBE_API_SECRET}
CUBEJS_JWT_ISSUER=formbricks-web
CUBEJS_JWT_AUDIENCE=formbricks-cube

# Email
MAIL_FROM=noreply@yourdomain.com
SMTP_HOST=smtp.yourprovider.com
SMTP_PORT=587
SMTP_USER=your_user
SMTP_PASSWORD=your_password

# Security (Production)
RATE_LIMITING_DISABLED=0
TELEMETRY_DISABLED=1
EMAIL_VERIFICATION_DISABLED=1
PASSWORD_RESET_DISABLED=1

Deployment Procedures

Initial Deployment

# 1. Generate all secrets
./scripts/generate-secrets.sh > .env.production.formbricks

# 2. Start PostgreSQL and Redis first
docker compose up -d postgres redis

# 3. Run database migrations (one-shot)
docker compose up formbricks-migrate
docker compose up formbricks-hub-migrate

# 4. Start Cube.js (analytics)
docker compose up -d formbricks-cube

# 5. Wait for Cube.js to be healthy
docker compose logs -f formbricks-cube | grep -q "Cube.js server is listening"

# 6. Start Hub API
docker compose up -d formbricks-hub

# 7. Start main web application
docker compose up -d formbricks

# 8. Verify all services
docker compose ps

Rolling Updates

# Update main web app (zero-downtime if replicas > 1)
docker compose pull formbricks
docker compose up -d --no-deps formbricks

# Update Hub API
docker compose pull formbricks-hub
docker compose up -d --no-deps formbricks-hub

# Update Cube.js (requires brief downtime)
docker compose pull formbricks-cube
docker compose up -d --no-deps formbricks-cube

Health Checks & Monitoring

Service Health Endpoints

Service Endpoint Expected Response
Web App GET http://localhost:3001/api/health { "status": "ok" }
Hub API GET http://localhost:8080/health { "status": "healthy" }
Cube.js GET http://localhost:4000/readyz 200 OK

Docker Health Checks (Automatic)

# In docker-compose.yml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
  interval: 30s
  timeout: 10s
  retries: 5
  start_period: 40s

Prometheus Metrics

Key metrics to alert on:

# Service up/down
up{job="formbricks-web"} == 0
up{job="formbricks-hub"} == 0
up{job="formbricks-cube"} == 0

# High error rate
rate(formbricks_webhook_errors_total[5m]) > 0.1

# Slow responses
histogram_quantile(0.95, rate(formbricks_http_request_duration_seconds_bucket[5m])) > 5

# Database connections
formbricks_db_connections_active / formbricks_db_connections_max > 0.8

Log Aggregation

# View all Formbricks logs
docker compose logs -f formbricks formbricks-hub formbricks-cube

# Filter for errors
docker compose logs formbricks 2>&1 | grep -i error

# Follow specific service
docker compose logs -f --tail=100 formbricks

Common Operations

Create New Environment (Per Workspace)

# 1. Access Formbricks Admin UI
# 2. Go to Settings β†’ Environments β†’ Create Environment
# 3. Copy Environment ID
# 4. Set in MicProtocol: NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=<env-id>

Configure Survey Targeting

# Via API (for automation)
curl -X POST https://formbricks.yourdomain.com/api/v1/surveys \
  -H "Authorization: Bearer ${FORMBRICKS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Post-Recording NPS",
    "type": "web",
    "questions": [...],
    "targeting": {
      "attributes": {
        "workspace_id": "eq:ws_123",
        "plan": "in:pro,enterprise"
      }
    }
  }'

Update Webhook URLs (After Domain Change)

# In Formbricks Admin: Settings β†’ Webhooks
# Update all webhook URLs to new domain
# Or via API:
curl -X PUT https://formbricks.yourdomain.com/api/v1/webhooks/${WEBHOOK_ID} \
  -H "Authorization: Bearer ${FORMBRICKS_API_KEY}" \
  -d '{"url": "https://api.yourdomain.com/integrations/webhooks/formbricks"}'

Backup Database

# Full backup
docker exec micprotocol-postgres pg_dump -U micprotocol formbricks > formbricks_backup_$(date +%Y%m%d).sql

# Compressed backup
docker exec micprotocol-postgres pg_dump -U micprotocol formbricks | gzip > formbricks_backup_$(date +%Y%m%d).sql.gz

# Restore
gunzip -c formbricks_backup_20240115.sql.gz | docker exec -i micprotocol-postgres psql -U micprotocol formbricks

Migrate to New PostgreSQL Instance

# 1. Dump current database
docker exec micprotocol-postgres pg_dump -U micprotocol formbricks > dump.sql

# 2. Create new database on new instance
createdb -h new-postgres -U micprotocol formbricks

# 3. Run migrations on new instance
docker run --rm \
  -e DATABASE_URL="postgresql://user:pass@new-postgres:5432/formbricks" \
  ghcr.io/formbricks/formbricks:latest \
  node packages/database/dist/scripts/apply-migrations.js

# 4. Restore data
psql -h new-postgres -U micprotocol formbricks < dump.sql

# 5. Update docker-compose.yml DATABASE_URL and restart
docker compose up -d formbricks-migrate formbricks-hub-migrate
docker compose up -d formbricks formbricks-hub formbricks-cube

Troubleshooting Guide

Issue: Web App Returns 500 / Won't Start

Symptoms: Container restarts, logs show database connection errors

Diagnosis:

# Check database connectivity
docker exec micprotocol-formbricks pg_isready -h postgres -U micprotocol

# Check migration status
docker compose logs formbricks-migrate

# Check environment variables
docker exec micprotocol-formbricks env | grep -E "DATABASE|NEXTAUTH|ENCRYPTION"

Resolution:

  1. Ensure formbricks-migrate completed successfully
  2. Verify DATABASE_URL includes ?schema=public
  3. Check NEXTAUTH_SECRET and ENCRYPTION_KEY are set
  4. Restart: docker compose restart formbricks

Issue: Hub API Unhealthy

Symptoms: Surveys don't load, widget shows "Failed to load survey"

Diagnosis:

# Check Hub logs
docker compose logs formbricks-hub

# Test Hub health
curl http://localhost:8080/health

# Check Hub database
docker exec micprotocol-formbricks-hub pg_isready -h postgres -U micprotocol

Resolution:

  1. Ensure formbricks-hub-migrate completed
  2. Verify HUB_API_KEY matches between web and hub
  3. Check Redis connectivity
  4. Restart: docker compose restart formbricks-hub

Issue: Cube.js Analytics Not Working

Symptoms: Dashboard shows no data, "Failed to load" errors

Diagnosis:

# Check Cube.js logs
docker compose logs formbricks-cube

# Test Cube.js API
curl -H "Authorization: ${CUBEJS_API_SECRET}" http://localhost:4000/readyz

# Check Cube.js can reach PostgreSQL
docker exec micprotocol-formbricks-cube pg_isready -h postgres -U micprotocol

Resolution:

  1. Verify CUBEJS_API_SECRET matches between web and cube
  2. Check CUBEJS_DB_* environment variables
  3. Ensure CUBEJS_JWT_ISSUER and CUBEJS_JWT_AUDIENCE match
  4. Restart: docker compose restart formbricks-cube

Issue: Webhooks Failing (401 Unauthorized)

Symptoms: MicProtocol API returns 401 for Formbricks webhooks

Diagnosis:

# Check webhook secret matches
grep FORMBRICKS_WEBHOOK_SECRET .env
# Compare with Formbricks Admin β†’ Settings β†’ Webhooks β†’ Secret

Resolution:

  1. Ensure FORMBRICKS_WEBHOOK_SECRET in .env matches Formbricks UI
  2. Regenerate if needed: openssl rand -hex 32
  3. Update both .env and Formbricks Admin
  4. Restart API: docker compose restart api

Issue: Rate Limiting Blocking Legitimate Requests

Symptoms: 429 responses, surveys not loading for some users

Diagnosis:

# Check rate limit config
docker exec micprotocol-formbricks env | grep RATE_LIMITING

# Check Redis for rate limit keys
docker exec micprotocol-redis redis-cli KEYS "ratelimit:*"

Resolution:

  1. Ensure RATE_LIMITING_DISABLED=0 in production
  2. Tune limits in Formbricks Admin β†’ Settings β†’ Rate Limiting
  3. For high-traffic: increase Redis capacity or use Redis Cluster

Issue: Email Notifications Not Sending

Symptoms: No survey invitation emails, password reset emails fail

Diagnosis:

# Check SMTP config
docker exec micprotocol-formbricks env | grep -i SMTP

# Check mailhog (dev) or SMTP logs
docker compose logs mailhog

Resolution:

  1. Verify SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD
  2. Test with: docker exec micprotocol-formbricks nc -zv smtp.example.com 587
  3. Check MAIL_FROM is verified in your SMTP provider

Disaster Recovery

Scenario: Complete Data Loss (PostgreSQL Corruption)

RTO: 2 hours | RPO: 24 hours (daily backups)

  1. Provision new PostgreSQL instance
  2. Restore latest backup:
    gunzip -c formbricks_backup_latest.sql.gz | psql -h new-pg -U micprotocol formbricks
    
  3. Run migrations to ensure schema is current:
    docker compose up formbricks-migrate formbricks-hub-migrate
    
  4. Restart all services:
    docker compose up -d formbricks-cube formbricks-hub formbricks
    
  5. Verify data integrity via Admin UI

Scenario: Formbricks Hub Database Corruption

RTO: 30 minutes | RPO: 1 hour

  1. Hub uses same PostgreSQL database (formbricks schema)
  2. Restore from backup (same as above)
  3. Re-run hub migrations:
    docker compose up formbricks-hub-migrate
    docker compose restart formbricks-hub
    

Scenario: Lost Encryption Keys (NEXTAUTH_SECRET, ENCRYPTION_KEY)

IMPACT: All existing sessions invalidated, encrypted data unrecoverable

Mitigation:

  1. These keys are never rotated after initial setup
  2. Store in secure password manager (1Password, Bitwarden)
  3. Document in runbook with access restricted to platform team

Recovery:

  1. Generate new keys
  2. Update .env and restart all services
  3. All users must re-authenticate
  4. Historical encrypted data (webhook payloads, etc.) will be unreadable

Security Checklist

Monthly

  • Rotate FORMBRICKS_CRON_SECRET and FORMBRICKS_WEBHOOK_SECRET
  • Review webhook delivery logs for anomalies
  • Check for failed login attempts in logs
  • Verify SSL certificates are valid (> 30 days)

Quarterly

  • Update all container images to latest patches
  • Review and prune old survey responses (GDPR)
  • Audit API keys in Formbricks Admin
  • Test backup restoration procedure

Annually

  • Rotate SMTP credentials
  • Review Redis AUTH password
  • Penetration test webhook endpoints
  • Update Cube.js and Hub to latest major versions

Performance Tuning

Database

-- Add indexes for common query patterns
CREATE INDEX idx_surveys_environment_status ON surveys(environment_id, status);
CREATE INDEX idx_responses_survey_created ON responses(survey_id, created_at);
CREATE INDEX idx_attributes_user_key ON user_attributes(user_id, key);

Redis

# Increase maxmemory for rate limiting
# In redis.conf or docker-compose:
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru

Cube.js

# In docker-compose.yml for formbricks-cube
environment:
  CUBEJS_CACHE_AND_QUEUE_DRIVER: redis
  CUBEJS_REDIS_URL: redis://:password@redis:6379
  CUBEJS_DB_MAX_POOL: 20

Contact & Escalation

Issue Severity Response Time Contact
P0 (All surveys down) 15 min Platform On-Call
P1 (Single workspace affected) 1 hour Platform Team
P2 (Analytics delayed) 4 hours Platform Team
P3 (Feature request) Next sprint Product Team

Runbook Owner: Platform Engineering
Last Updated: 2024-01-15
Next Review: 2024-04-15