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)
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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)
```yaml
# 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:
```promql
# 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
```bash
# 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)
```bash
# 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
```bash
# 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)
```bash
# 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
```bash
# 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
```bash
# 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:**
```bash
# 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:**
```bash
# 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:**
```bash
# 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:**
```bash
# 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:**
```bash
# 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:**
```bash
# 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:
```bash
gunzip -c formbricks_backup_latest.sql.gz | psql -h new-pg -U micprotocol formbricks
```
3. Run migrations to ensure schema is current:
```bash
docker compose up formbricks-migrate formbricks-hub-migrate
```
4. Restart all services:
```bash
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:
```bash
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
```sql
-- 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
```bash
# Increase maxmemory for rate limiting
# In redis.conf or docker-compose:
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
```
### Cube.js
```yaml
# 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