Mo - Deployment & Launch Document
1. Infrastructure Overview
┌─────────────────────────────────────────────────────────────────┐
│ VERCEL │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Next.js App │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Pages │ │ API │ │ Edge │ │ Static │ │ │
│ │ │ (SSR) │ │ Routes │ │Functions│ │ Assets │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
└──────────────────────────────┼───────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ NEON │ │ UPSTASH │ │ CLERK │
│ PostgreSQL │ │ Redis │ │ Auth │
│ (Database) │ │ (Cache) │ │ (Users) │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ANTHROPIC │ │ STRIPE │ │ RESEND │
│ Claude API │ │ (Payments) │ │ (Email) │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ SENTRY │ │ POSTHOG │ │ VERCEL BLOB │
│ (Errors) │ │ (Analytics) │ │ (Storage) │
└──────────────┘ └──────────────┘ └──────────────┘
2. Service Accounts to Create
| Service | URL | Purpose | Tier |
|---|---|---|---|
| Vercel | vercel.com | Hosting, deployment | Free → Pro |
| Neon | neon.tech | PostgreSQL database | Free |
| Clerk | clerk.com | Authentication | Free |
| Stripe | stripe.com | Payments | Pay as you go |
| Anthropic | anthropic.com | Claude AI API | Pay as you go |
| Upstash | upstash.com | Redis cache | Free |
| Resend | resend.com | Transactional email | Free |
| Sentry | sentry.io | Error tracking | Free |
| PostHog | posthog.com | Analytics | Free |
| GitHub | github.com | Code repository | Free |
Account Setup Order
1. GitHub → Create repo first
2. Vercel → Connect to GitHub
3. Neon → Create database
4. Clerk → Setup auth
5. Stripe → Setup payments (can do later)
6. Anthropic → Get API key
7. Upstash → Create Redis instance
8. Resend → Setup email domain
9. Sentry → Create project
10. PostHog → Create project
3. Environment Strategy
| Environment | URL | Branch | Database | Purpose |
|---|---|---|---|---|
| Production | mo.fitness | main | Neon main | Live users |
| Preview | *.vercel.app | PR branches | Neon main* | Review changes |
| Development | localhost:3000 | local | Neon branch | Your machine |
*Preview deployments use production DB by default. For isolated testing, use Neon branching.
Neon Database Branching
# Create a development branch
neon branches create --name dev
# Each branch is a full copy with its own connection string
# Great for testing migrations without affecting production
4. Environment Variables
Required Variables
# ============================================
# DATABASE (Neon)
# ============================================
DATABASE_URL="postgresql://user:pass@host/db?sslmode=require"
# ============================================
# AUTHENTICATION (Clerk)
# ============================================
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_live_xxx"
CLERK_SECRET_KEY="sk_live_xxx"
CLERK_WEBHOOK_SECRET="whsec_xxx"
# Clerk URLs
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/login"
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/signup"
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/dashboard"
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/onboarding"
# ============================================
# AI (Anthropic Claude)
# ============================================
ANTHROPIC_API_KEY="sk-ant-xxx"
# ============================================
# PAYMENTS (Stripe)
# ============================================
STRIPE_SECRET_KEY="sk_live_xxx"
STRIPE_WEBHOOK_SECRET="whsec_xxx"
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_live_xxx"
STRIPE_PRO_PRICE_ID="price_xxx"
# ============================================
# CACHE (Upstash Redis)
# ============================================
UPSTASH_REDIS_REST_URL="https://xxx.upstash.io"
UPSTASH_REDIS_REST_TOKEN="xxx"
# ============================================
# STORAGE (Vercel Blob)
# ============================================
BLOB_READ_WRITE_TOKEN="vercel_blob_xxx"
# ============================================
# EMAIL (Resend)
# ============================================
RESEND_API_KEY="re_xxx"
# ============================================
# MONITORING (Sentry)
# ============================================
SENTRY_DSN="https://[email protected]/xxx"
NEXT_PUBLIC_SENTRY_DSN="https://[email protected]/xxx"
SENTRY_AUTH_TOKEN="sntrys_xxx"
SENTRY_ORG="your-org"
SENTRY_PROJECT="mo"
# ============================================
# ANALYTICS (PostHog)
# ============================================
NEXT_PUBLIC_POSTHOG_KEY="phc_xxx"
NEXT_PUBLIC_POSTHOG_HOST="https://app.posthog.com"
# ============================================
# APP CONFIG
# ============================================
NEXT_PUBLIC_APP_URL="https://mo.fitness"
NEXT_PUBLIC_APP_NAME="Mo"
Setting Variables in Vercel
# Via CLI
vercel env add DATABASE_URL production
vercel env add DATABASE_URL preview
vercel env add DATABASE_URL development
# Or use Vercel Dashboard:
# Project Settings → Environment Variables
Local Development (.env.local)
# Copy from .env.example
cp .env.example .env.local
# Fill in your development values
# This file is gitignored
5. CI/CD Pipeline
GitHub Actions Workflow
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
DATABASE_URL: ${{ secrets.DATABASE_URL_TEST }}
jobs:
# ==========================================
# QUALITY CHECKS
# ==========================================
quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint
run: pnpm lint
- name: Type check
run: pnpm typecheck
- name: Format check
run: pnpm format:check
# ==========================================
# UNIT & INTEGRATION TESTS
# ==========================================
test:
name: Tests
runs-on: ubuntu-latest
needs: quality
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run tests
run: pnpm test --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
if: always()
with:
files: ./coverage/lcov.info
# ==========================================
# E2E TESTS
# ==========================================
e2e:
name: E2E Tests
runs-on: ubuntu-latest
needs: quality
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium
- name: Run E2E tests
run: pnpm test:e2e
env:
PLAYWRIGHT_TEST_BASE_URL: ${{ secrets.PREVIEW_URL }}
- name: Upload test results
if: failure()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: playwright-report/
retention-days: 7
# ==========================================
# BUILD CHECK
# ==========================================
build:
name: Build
runs-on: ubuntu-latest
needs: [test, e2e]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
env:
SKIP_ENV_VALIDATION: true
Vercel Deployment (Automatic)
Vercel automatically deploys:
- Production: On push to
main - Preview: On every PR
No additional configuration needed - just connect GitHub repo to Vercel.
6. Database Management
Drizzle Configuration
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './lib/db/schema.ts',
out: './lib/db/migrations',
driver: 'pg',
dbCredentials: {
connectionString: process.env.DATABASE_URL!,
},
verbose: true,
strict: true,
});
Migration Scripts
// package.json
{
"scripts": {
"db:generate": "drizzle-kit generate:pg",
"db:push": "drizzle-kit push:pg",
"db:migrate": "tsx lib/db/migrate.ts",
"db:migrate:prod": "NODE_ENV=production tsx lib/db/migrate.ts",
"db:studio": "drizzle-kit studio",
"db:seed": "tsx lib/db/seed.ts",
"db:reset": "tsx lib/db/reset.ts"
}
}
Migration Runner
// lib/db/migrate.ts
import { drizzle } from 'drizzle-orm/neon-http';
import { migrate } from 'drizzle-orm/neon-http/migrator';
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
const db = drizzle(sql);
async function main() {
console.log('Running migrations...');
await migrate(db, { migrationsFolder: './lib/db/migrations' });
console.log('Migrations complete!');
process.exit(0);
}
main().catch((err) => {
console.error('Migration failed!', err);
process.exit(1);
});
Seed Script
// lib/db/seed.ts
import { db } from './index';
import { coaches, exercises, programs } from './schema';
import { coachesData } from './data/coaches';
import { exercisesData } from './data/exercises';
import { programsData } from './data/programs';
async function seed() {
console.log('Seeding database...');
// Seed coaches
await db.insert(coaches).values(coachesData);
console.log(`✓ Seeded ${coachesData.length} coaches`);
// Seed exercises
await db.insert(exercises).values(exercisesData);
console.log(`✓ Seeded ${exercisesData.length} exercises`);
// Seed programs
await db.insert(programs).values(programsData);
console.log(`✓ Seeded programs`);
console.log('Seeding complete!');
}
seed().catch(console.error);
Migration Workflow
# Development: Make schema changes
# 1. Edit lib/db/schema.ts
# 2. Generate migration
pnpm db:generate
# 3. Review generated SQL in lib/db/migrations/
# 4. Apply to dev database
pnpm db:push
# Production: Apply migrations
# 1. Merge PR to main
# 2. Run migration before deploy
pnpm db:migrate:prod
# 3. Vercel auto-deploys
7. Monitoring & Observability
Sentry Setup
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
// Performance monitoring
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
// Session replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
integrations: [
new Sentry.Replay({
maskAllText: false,
blockAllMedia: false,
}),
],
});
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
});
// sentry.edge.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
});
PostHog Analytics
// lib/analytics/posthog.ts
import posthog from 'posthog-js';
export function initPostHog() {
if (typeof window !== 'undefined' && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
capture_pageview: false, // We capture manually for SPA
capture_pageleave: true,
});
}
}
// Track events
export function trackEvent(event: string, properties?: Record<string, any>) {
posthog.capture(event, properties);
}
// Track page views
export function trackPageView(url: string) {
posthog.capture('$pageview', { $current_url: url });
}
// Identify users
export function identifyUser(userId: string, traits?: Record<string, any>) {
posthog.identify(userId, traits);
}
Health Check Endpoint
// app/api/health/route.ts
import { db } from '@/lib/db';
import { redis } from '@/lib/redis';
import { NextResponse } from 'next/server';
export const runtime = 'edge';
export async function GET() {
const health: {
status: 'ok' | 'degraded' | 'down';
timestamp: string;
version: string;
checks: Record<string, 'ok' | 'error'>;
} = {
status: 'ok',
timestamp: new Date().toISOString(),
version: process.env.VERCEL_GIT_COMMIT_SHA?.slice(0, 7) || 'dev',
checks: {},
};
// Check database
try {
await db.execute('SELECT 1');
health.checks.database = 'ok';
} catch {
health.checks.database = 'error';
health.status = 'degraded';
}
// Check Redis
try {
await redis.ping();
health.checks.redis = 'ok';
} catch {
health.checks.redis = 'error';
health.status = 'degraded';
}
const statusCode = health.status === 'ok' ? 200 : 503;
return NextResponse.json(health, { status: statusCode });
}
Alert Configuration
| Alert | Condition | Action |
|---|---|---|
| Error rate spike | > 10 errors/min | Email notification |
| P95 latency | > 2000ms | Email notification |
| Database errors | Any connection failure | Email + SMS |
| Payment failures | Any Stripe webhook error | Email + SMS |
| Auth failures | > 5 failed logins/user/hour | |
| Health check fail | 2 consecutive failures | Email + SMS |
8. Security Configuration
Security Headers (next.config.js)
// next.config.js
const securityHeaders = [
{
key: 'X-DNS-Prefetch-Control',
value: 'on',
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload',
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()',
},
];
/** @type {import('next').NextConfig} */
const nextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: securityHeaders,
},
];
},
};
module.exports = nextConfig;
Rate Limiting (Upstash)
// lib/rate-limit.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
// General API rate limit
export const apiRateLimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(100, '1 m'), // 100 requests per minute
analytics: true,
});
// Auth rate limit (stricter)
export const authRateLimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, '1 m'), // 10 requests per minute
analytics: true,
});
// AI chat rate limit
export const chatRateLimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(30, '1 m'), // 30 messages per minute
analytics: true,
});
Input Validation (Zod)
// lib/validations/workout.ts
import { z } from 'zod';
export const logSetSchema = z.object({
exerciseId: z.string().uuid(),
setNumber: z.number().int().min(1).max(20),
weight: z.number().min(0).max(2000),
reps: z.number().int().min(0).max(100),
rpe: z.number().min(1).max(10).optional(),
notes: z.string().max(500).optional(),
});
export const startWorkoutSchema = z.object({
programDayId: z.string().uuid(),
date: z.string().datetime().optional(),
});
Security Checklist
Authentication:
□ Clerk handles all auth (OAuth, sessions, JWT)
□ Webhook signatures validated
□ Session expiration configured
□ OAuth state validation (Clerk handles)
API Security:
□ Rate limiting on all endpoints
□ Input validation with Zod
□ SQL injection prevented (Drizzle ORM)
□ CORS configured for domain only
□ API routes check authentication
Data Security:
□ HTTPS enforced (Vercel)
□ Database encrypted at rest (Neon)
□ Secrets in environment variables only
□ No sensitive data in logs
□ PII handling compliant
Headers:
□ HSTS enabled
□ X-Frame-Options set
□ X-Content-Type-Options set
□ CSP configured
□ Referrer-Policy set
Payments:
□ Stripe webhook signature validation
□ Idempotency keys for charges
□ No card data touches our servers
Privacy:
□ GDPR data export available
□ Account deletion available
□ Privacy policy accessible
□ Cookie consent (if needed)
9. Performance Optimization
Next.js Optimizations
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Image optimization
images: {
remotePatterns: [
{ hostname: 'img.clerk.com' },
{ hostname: '*.vercel-storage.com' },
],
formats: ['image/avif', 'image/webp'],
},
// Experimental features
experimental: {
optimizeCss: true,
},
// Compression
compress: true,
// Power optimizations
poweredByHeader: false,
};
module.exports = nextConfig;
Bundle Analysis
// package.json
{
"scripts": {
"analyze": "ANALYZE=true pnpm build"
}
}
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer(nextConfig);
Performance Targets
| Metric | Target | Measurement |
|---|---|---|
| Lighthouse Performance | > 90 | Lighthouse CI |
| Lighthouse Accessibility | > 90 | Lighthouse CI |
| Lighthouse Best Practices | > 90 | Lighthouse CI |
| Lighthouse SEO | > 90 | Lighthouse CI |
| Lighthouse PWA | > 90 | Lighthouse CI |
| First Contentful Paint | < 1.5s | Web Vitals |
| Largest Contentful Paint | < 2.5s | Web Vitals |
| Cumulative Layout Shift | < 0.1 | Web Vitals |
| First Input Delay | < 100ms | Web Vitals |
| Time to First Byte | < 200ms | Vercel Analytics |
| API p95 Response Time | < 200ms | Custom logging |
Caching Strategy
// API route caching
export const revalidate = 60; // Revalidate every 60 seconds
// Static page caching
export const dynamic = 'force-static';
// Dynamic with ISR
export const revalidate = 3600; // Revalidate every hour
10. Domain & DNS Setup
Domain Configuration
| Record | Type | Name | Value | TTL |
|---|---|---|---|---|
| A | @ | 76.76.21.21 | 300 | |
| CNAME | www | cname.vercel-dns.com | 300 | |
| TXT | @ | vercel-verification=xxx | 300 | |
| MX | @ | feedback-smtp.us-east-1.amazonses.com | 300 | |
| TXT | @ | v=spf1 include:amazonses.com ~all | 300 |
SSL Certificate
Vercel automatically provisions and renews SSL certificates via Let's Encrypt.
Vercel Domain Setup
# Add domain via CLI
vercel domains add mo.fitness
# Or via Dashboard:
# Project Settings → Domains → Add
11. Backup Strategy
Database Backups (Neon)
| Feature | Free Tier | Pro Tier |
|---|---|---|
| Point-in-time recovery | 7 days | 30 days |
| Branching | Unlimited | Unlimited |
| Export | Manual | Manual |
Manual Backup Script
#!/bin/bash
# scripts/backup-db.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_${DATE}.sql"
# Export database
pg_dump $DATABASE_URL > $BACKUP_FILE
# Compress
gzip $BACKUP_FILE
# Upload to storage (optional)
# aws s3 cp ${BACKUP_FILE}.gz s3://mo-backups/
User Data Export
// app/api/v1/users/me/export/route.ts
export async function GET(request: Request) {
const user = await getCurrentUser();
// Gather all user data
const userData = {
profile: await getUserProfile(user.id),
workouts: await getUserWorkouts(user.id),
weightEntries: await getUserWeightEntries(user.id),
personalRecords: await getUserPRs(user.id),
chatHistory: await getUserChatHistory(user.id),
exportedAt: new Date().toISOString(),
};
return new Response(JSON.stringify(userData, null, 2), {
headers: {
'Content-Type': 'application/json',
'Content-Disposition': `attachment; filename="mo-data-export-${user.id}.json"`,
},
});
}
12. Rollback Procedures
Vercel Instant Rollback
# Via CLI
vercel rollback
# Via Dashboard
# Deployments → Select previous deployment → "..." → Promote to Production
Database Rollback
# Option 1: Point-in-time recovery (Neon Dashboard)
# Restore to specific timestamp
# Option 2: Restore from branch
neon branches restore main --source dev
# Option 3: Run down migration
pnpm db:migrate:down
Feature Flags (Emergency Kill Switch)
// lib/feature-flags.ts
import { PostHog } from 'posthog-node';
const posthog = new PostHog(process.env.POSTHOG_API_KEY!);
export async function isFeatureEnabled(
feature: string,
userId: string
): Promise<boolean> {
return await posthog.isFeatureEnabled(feature, userId);
}
// Usage in code
if (await isFeatureEnabled('ai-coach', user.id)) {
// Show AI coach
}
Rollback Decision Matrix
| Severity | Symptoms | Action |
|---|---|---|
| P0 Critical | App down, data loss risk | Immediate rollback |
| P1 High | Major feature broken | Rollback within 1 hour |
| P2 Medium | Minor feature broken | Hotfix or rollback |
| P3 Low | UI issue, no data impact | Fix forward |
13. Launch Checklist
Week Before Launch
Infrastructure:
□ All environment variables set in Vercel
□ Custom domain configured and SSL active
□ Database migrations run on production
□ Seed data loaded (coaches, exercises, programs)
□ Redis cache warmed up
Testing:
□ All E2E tests passing
□ Manual testing complete on production
□ Performance audit passed (Lighthouse > 90)
□ Security audit complete
□ Load testing done (100 concurrent users)
Integrations:
□ Stripe production mode enabled
□ Stripe webhook endpoint configured
□ Clerk production instance
□ OAuth providers configured (Google, Apple)
□ Email sending verified (Resend)
Monitoring:
□ Sentry connected and receiving errors
□ PostHog tracking events
□ Vercel Analytics enabled
□ Health check endpoint responding
□ Alert notifications configured
Legal:
□ Privacy Policy page live
□ Terms of Service page live
□ Cookie consent (if required)
Content:
□ All copy reviewed
□ Social preview images (og:image)
□ Favicon and app icons
□ 404 page styled
□ Error pages styled
Day Before Launch
□ Final smoke test on production
□ Test complete user flow:
□ Visit landing page
□ Sign up with email
□ Sign up with Google
□ Complete onboarding
□ Start a workout
□ Log sets
□ Complete workout
□ Log weight
□ Chat with coach
□ Upgrade to Pro
□ Cancel subscription
□ Verify Stripe webhooks receiving
□ Check all monitoring dashboards
□ Prepare rollback plan
□ Brief support plan (email ready)
□ Draft social media announcements
□ Get good sleep!
Launch Day
Hour 0:
□ Final health check
□ Remove any beta banners
□ Verify production is stable
Hour 1:
□ Announce on social media
□ Send to initial users/testers
□ Monitor Sentry for errors
□ Monitor Vercel for performance
Hours 2-8:
□ Monitor error rates closely
□ Monitor server performance
□ Respond to any issues immediately
□ Collect early feedback
End of Day:
□ Review metrics
□ Fix any critical issues
□ Plan next day priorities
□ Celebrate! 🎉
Post-Launch (24-48 hours)
□ Monitor error rates trend
□ Check user signup numbers
□ Review any support requests
□ Fix critical bugs immediately
□ Collect and organize feedback
□ Thank early users
□ Plan first iteration
14. Scaling Considerations
Service Limits (Free Tiers)
| Service | Free Limit | Upgrade Trigger |
|---|---|---|
| Vercel | 100GB bandwidth | > 1,000 daily users |
| Neon | 0.5GB storage, 190 compute hours | > 500 active users |
| Clerk | 10,000 MAU | > 5,000 users |
| Upstash | 10,000 requests/day | > 1,000 daily active |
| Resend | 3,000 emails/month | > 2,000 users |
| Sentry | 5,000 errors/month | High error rate |
| PostHog | 1M events/month | > 5,000 users |
Cost Estimates at Scale
| Users | Vercel | Neon | Clerk | Anthropic | Total/mo |
|---|---|---|---|---|---|
| 100 | Free | Free | Free | ~$10 | ~$10 |
| 1,000 | Free | Free | Free | ~$100 | ~$100 |
| 10,000 | $20 | $19 | $25 | ~$500 | ~$564 |
| 50,000 | $20 | $69 | $99 | ~$2,000 | ~$2,188 |
*Anthropic costs depend heavily on chat usage. Optimize prompts early.
Optimization Strategies
1. Prompt Optimization
- Shorter system prompts
- Cache common responses
- Limit conversation history
2. Database Optimization
- Add indexes as queries grow
- Archive old data
- Use connection pooling
3. Caching
- Redis for frequent queries
- Static generation where possible
- CDN for assets
4. Code Splitting
- Lazy load heavy components
- Dynamic imports for 3D
- Route-based splitting
15. Support & Maintenance
Support Channels
| Channel | Tool | Response Time |
|---|---|---|
| [email protected] | < 24 hours | |
| In-app feedback | PostHog surveys | < 48 hours |
| @mo_fitness | < 12 hours | |
| Help docs | /help page | Self-service |
Maintenance Windows
Scheduled Maintenance:
- Time: Tuesdays 2-4 AM EST
- Frequency: As needed (aim for < 1/month)
- Notice: 48 hours minimum
- Status page: status.mo.fitness (optional)
On-Call Rotation
For solo development:
- Monitor Sentry/Vercel notifications
- Set up PagerDuty or similar for P0 alerts
- Define "office hours" for non-critical issues
- Have rollback plan ready
Document Status
- Infrastructure Overview
- Service Accounts
- Environment Strategy
- Environment Variables
- CI/CD Pipeline
- Database Management
- Monitoring & Observability
- Security Configuration
- Performance Optimization
- Domain & DNS Setup
- Backup Strategy
- Rollback Procedures
- Launch Checklist
- Scaling Considerations
- Support & Maintenance
Phase 6: Deployment & Launch - COMPLETE
Document created: December 2024 Version: 1.0 Status: Complete