Deployment Guide
This guide walks you through deploying OneLibro to production using Vercel, Supabase, and Plaid. By the end, you'll have OneLibro running with three fully functional domains: your main portfolio, the finance app, and the admin dashboard.
Overview
OneLibro uses a multi-subdomain architecture where different applications are served from the same Next.js project:
- Main Domain (
yatheeshnagella.com) - Portfolio site - Finance Subdomain (
finance.yatheeshnagella.com) - Personal finance application - Admin Subdomain (
admin.yatheeshnagella.com) - Admin dashboard
Key Technologies:
- Hosting: Vercel (Next.js 15 with Turbopack)
- Database: Supabase (PostgreSQL)
- Banking Integration: Plaid API
- Email: Resend
- Cron Jobs: Vercel Cron
Prerequisites
Before deploying, ensure you have:
- GitHub Account: Repository with OneLibro code
- Vercel Account: Free or Pro account (sign up at vercel.com)
- Supabase Account: Free tier works (supabase.com)
- Plaid Account: Development or Production account (plaid.com)
- Resend Account: Free tier works (resend.com)
- Domain Name: Registered and ready to configure DNS
Phase 1: Supabase Production Setup
Step 1: Create Production Supabase Project
1. Create New Project:
- Go to Supabase Dashboard
- Click "New Project"
- Fill in details:
- Name:
oneLibro-production(or your choice) - Database Password: Generate strong password (save this!)
- Region: Choose closest to your users (e.g.,
us-east-1) - Plan: Free tier works for MVP, Pro for scale
- Name:
- Click "Create Project"
- Wait for provisioning (2-3 minutes)
2. Save Credentials:
After project is created, save these values:
# Find these in Settings > API
NEXT_PUBLIC_SUPABASE_URL=https://[your-project-ref].supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... (long token)
SUPABASE_SERVICE_ROLE_KEY=eyJ... (different long token)
Important: The service role key has admin access - NEVER expose to client!
Step 2: Run Database Migrations
OneLibro requires several database tables. Run all migrations:
Option 1: Using Supabase SQL Editor:
- Go to SQL Editor in Supabase dashboard
- Click "New Query"
- Copy each migration file from
supabase/migrations/in your codebase - Execute migrations in order (oldest to newest)
Tables Created:
users- User profiles with invite trackinginvite_codes- Invite code systeminvite_code_requests- Self-service invite requestsplaid_items- Connected bank institutionsaccounts- User bank accountstransactions- Financial transactionsbudgets- User budgetsbudget_alert_history- Budget alert trackingemail_templates- Email template managementemail_logs- Email sending historyemail_campaigns- Bulk email campaigns (if using)notification_preferences- User email preferences
Option 2: Using Supabase CLI (if you have migrations in code):
# Install Supabase CLI
npm install -g supabase
# Login to Supabase
supabase login
# Link to your project
supabase link --project-ref [your-project-ref]
# Push migrations to production
supabase db push
Step 3: Verify Row-Level Security (RLS)
OneLibro uses RLS policies for security. Verify they're enabled:
- Go to Database > Tables
- For each table, check "Row Level Security" is enabled
- Review policies in Authentication > Policies
Key Policies:
- Users can only read/update their own data
- Admins have full access (checked via
is_adminflag) - Invite codes publicly readable for validation
- Transactions/accounts/budgets scoped to user
Step 4: Configure Authentication
Enable Email Auth:
- Go to Authentication > Providers
- Enable Email provider
- Configure email templates (optional):
- Confirmation email
- Password reset
- Magic link
SMTP Settings (optional, for custom emails):
- Supabase uses its own SMTP by default
- For custom domain emails, configure SMTP in Settings > Auth
Phase 2: Plaid Production Setup
Step 1: Upgrade to Plaid Development/Production
Sandbox vs Development vs Production:
| Environment | Cost | Limits | Use Case |
|---|---|---|---|
| Sandbox | Free | Unlimited | Local testing only |
| Development | Free | 100 items | Pre-launch testing with real banks |
| Production | Paid | Unlimited | Live application |
For initial deployment, use Development environment.
Step 2: Get Production Credentials
1. Apply for Development Access:
- Log in to Plaid Dashboard
- Navigate to Team Settings > Keys
- Request access to Development or Production environment
- Fill out application (business info, use case)
- Wait for approval (typically 1-2 business days)
2. Generate API Keys:
Once approved:
- Go to Team Settings > Keys
- Copy your Development or Production credentials:
PLAID_CLIENT_ID=your_client_id
PLAID_SECRET=your_development_or_production_secret
PLAID_ENV=development # or 'production'
Important: Never commit these to Git!
Step 3: Configure Webhook URL
Plaid sends real-time updates via webhooks:
1. Set Webhook URL in Plaid Dashboard:
- Go to Team Settings > Webhooks
- Add webhook URL:
https://yourdomain.com/api/plaid/webhook - Select events to receive (recommended: all transaction events)
2. Get Verification Key:
- Copy Webhook Verification Key from dashboard
- Save for environment variables:
PLAID_WEBHOOK_URL=https://yourdomain.com/api/plaid/webhook
PLAID_WEBHOOK_VERIFICATION_KEY=your_verification_key
Phase 3: Resend Email Setup
Step 1: Create Resend Account
- Sign up at Resend
- Verify your email address
- Choose free tier (3,000 emails/month)
Step 2: Generate API Key
- Go to API Keys in Resend dashboard
- Click "Create API Key"
- Name:
OneLibro Production - Permissions: Full Access
- Copy the API key:
RESEND_API_KEY=re_... (starts with 're_')
Important: This key is shown only once - save it now!
Step 3: Configure Sender Email
Option 1: Use Resend's Test Domain (quick start):
RESEND_FROM_EMAIL=onboarding@resend.dev
RESEND_FROM_NAME=OneLibro
Option 2: Use Your Custom Domain (recommended for production):
- Go to Domains in Resend dashboard
- Click "Add Domain"
- Enter your domain (e.g.,
yatheeshnagella.com) - Add DNS records provided by Resend to your domain registrar:
- TXT record for verification
- MX records for receiving
- DKIM records for authentication
- Wait for verification (can take up to 48 hours)
- Set sender email:
RESEND_FROM_EMAIL=noreply@yatheeshnagella.com
RESEND_FROM_NAME=OneLibro
Phase 4: Vercel Deployment
Step 1: Connect GitHub Repository
1. Import Project:
- Log in to Vercel Dashboard
- Click "Add New" > "Project"
- Select your GitHub repository
- Click "Import"
2. Configure Project:
- Framework Preset: Next.js (auto-detected)
- Root Directory:
./(default) - Build Command:
npm run build(default) - Output Directory:
.next(default) - Install Command:
npm ci(default)
Step 2: Configure Environment Variables
CRITICAL: Add all environment variables before first deployment.
1. Go to Project Settings:
- Click on your project
- Go to Settings > Environment Variables
2. Add Required Variables:
Supabase:
NEXT_PUBLIC_SUPABASE_URL=https://[project].supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
Plaid:
PLAID_CLIENT_ID=your_client_id
PLAID_SECRET=your_secret
PLAID_ENV=development # or production
PLAID_WEBHOOK_URL=https://yourdomain.com/api/plaid/webhook
PLAID_WEBHOOK_VERIFICATION_KEY=your_key # Optional
Encryption (generate with openssl rand -hex 32):
ENCRYPTION_KEY=64_character_hex_string
Email (Resend):
RESEND_API_KEY=re_...
RESEND_FROM_EMAIL=noreply@yourdomain.com
RESEND_FROM_NAME=OneLibro
Cron Jobs (generate random secret):
CRON_SECRET=your_random_secret_string
App Configuration:
NEXT_PUBLIC_APP_URL=https://yourdomain.com
NODE_ENV=production
3. Set Environment Scope:
- Select Production for all variables (required)
- Optionally also add to Preview and Development
4. Save Variables:
- Click "Save" after adding all variables
Step 3: Deploy
Initial Deployment:
- Click "Deploy" in Vercel dashboard
- Vercel will:
- Install dependencies (
npm ci) - Build Next.js app (
npm run build) - Deploy to production
- Install dependencies (
- Wait for build to complete (2-5 minutes)
- Check build logs for errors
Build Success:
✓ Compiled successfully
✓ Linting and checking validity of types
✓ Collecting page data
✓ Generating static pages (61/61)
✓ Finalizing page optimization
Route (app) Size First Load JS
┌ ○ / 142 B 87.3 kB
├ ○ /admin 1.42 kB 102 kB
├ ○ /finance 9.86 kB 110 kB
└ ○ /finance/dashboard 12.1 kB 113 kB
Build Failures:
- Check environment variables are set correctly
- Review build logs for missing dependencies
- Verify TypeScript types match database schema
Phase 5: Domain Configuration
Step 1: Configure Main Domain
1. Add Domain in Vercel:
- Go to Project Settings > Domains
- Click "Add Domain"
- Enter your domain:
yatheeshnagella.com - Click "Add"
2. Configure DNS:
Vercel will show DNS records to add. Go to your domain registrar and add:
Option 1: A Record (recommended):
Type: A
Name: @
Value: 76.76.21.21
Option 2: CNAME Record:
Type: CNAME
Name: @
Value: cname.vercel-dns.com
3. Verify:
- Wait for DNS propagation (can take up to 48 hours, usually 5-10 minutes)
- Vercel will automatically detect and verify
- SSL certificate auto-generated by Vercel
Step 2: Configure Subdomains
Add Finance Subdomain:
- In Domains section, click "Add Domain"
- Enter:
finance.yatheeshnagella.com - Click "Add"
- Add DNS record at your registrar:
Type: CNAME
Name: finance
Value: cname.vercel-dns.com
Add Admin Subdomain:
- Click "Add Domain" again
- Enter:
admin.yatheeshnagella.com - Click "Add"
- Add DNS record:
Type: CNAME
Name: admin
Value: cname.vercel-dns.com
Verify Subdomain Routing:
OneLibro uses middleware to route subdomains. After DNS propagates:
- Visit
https://finance.yourdomain.com→ Should show finance app - Visit
https://admin.yourdomain.com→ Should show admin dashboard - Visit
https://yourdomain.com→ Should show portfolio
How It Works (from middleware.ts):
// Subdomain routing handled in middleware
if (hostname.startsWith('admin.')) {
url.pathname = `/admin${url.pathname}`;
return NextResponse.rewrite(url);
}
if (hostname.startsWith('finance.')) {
url.pathname = `/finance${url.pathname}`;
return NextResponse.rewrite(url);
}
Phase 6: Cron Jobs Setup
OneLibro uses Vercel Cron to send daily budget alerts.
Step 1: Configure Cron Job in Vercel
1. Create vercel.json (if not exists):
In your project root, create or update vercel.json:
{
"crons": [
{
"path": "/api/cron/budget-alerts",
"schedule": "0 9 * * *"
}
]
}
Schedule Format (cron syntax):
0 9 * * *= Daily at 9:00 AM UTC0 */6 * * *= Every 6 hours0 0 * * 0= Weekly on Sunday at midnight
2. Deploy Configuration:
- Commit
vercel.jsonto repository - Push to GitHub
- Vercel auto-deploys and configures cron
3. Verify in Dashboard:
- Go to Project > Settings > Cron Jobs
- Verify job appears:
Budget Alerts - Daily at 9:00 AM UTC - Check Last Run and Status
Step 2: Secure Cron Endpoint
The budget alerts endpoint (/api/cron/budget-alerts) should only be accessible by Vercel Cron.
Authentication (from your API route):
// api/cron/budget-alerts/route.ts
export async function GET(request: Request) {
// Verify request is from Vercel Cron
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response('Unauthorized', { status: 401 });
}
// Run budget alert logic
// ...
}
Vercel Configuration:
Vercel automatically adds the Authorization: Bearer <CRON_SECRET> header to cron requests. Make sure CRON_SECRET is set in environment variables.
Step 3: Test Cron Job
Manual Testing:
# Call endpoint manually with CRON_SECRET
curl -X GET https://yourdomain.com/api/cron/budget-alerts \
-H "Authorization: Bearer your_cron_secret"
Expected Response:
{
"success": true,
"alertsSent": 5,
"message": "Budget alerts sent successfully"
}
Monitor Execution:
- Check Vercel Logs for cron execution
- Check Resend Dashboard for sent emails
- Check
budget_alert_historytable in Supabase
Phase 7: Post-Deployment Verification
Verification Checklist
1. Domain Access:
- Main domain loads:
https://yourdomain.com - Finance subdomain loads:
https://finance.yourdomain.com - Admin subdomain loads:
https://admin.yourdomain.com - SSL certificates working (https, no warnings)
- Subdomain routing works correctly (each shows correct app)
2. Authentication:
- User signup works on finance app
- Invite code validation works
- Login works with email/password
- Admin login works with TOTP 2FA
- Session persistence works (refresh doesn't log out)
3. Plaid Integration:
- Plaid Link opens when clicking "Connect Bank Account"
- Can search for bank in Plaid Link
- Can complete authentication flow
- Accounts appear after connection
- Transactions sync successfully
- Account balances display correctly
4. Email System:
- Welcome email sent on signup
- Invite request confirmation sent
- Budget alert emails sent (check cron execution)
- Emails arrive in inbox (not spam)
- Email templates render correctly
- Unsubscribe links work
5. Database:
- New users saved to
userstable - Plaid items saved with encrypted access tokens
- Accounts and transactions synced
- Budgets can be created and saved
- RLS policies enforced (users can't see others' data)
6. Cron Jobs:
- Budget alerts cron job configured in Vercel
- First execution successful (check logs)
- Emails sent when budgets exceed threshold
- Duplicate alerts prevented (check
budget_alert_history)
Testing User Flows
New User Signup Flow:
- Go to
https://finance.yourdomain.com - Click "Don't have an invite code? Request one"
- Submit email for invite request
- Check email for confirmation
- Admin approves request (admin dashboard)
- User receives invite code email
- User signs up with invite code
- Receives welcome email
- Redirected to dashboard
Bank Connection Flow:
- Login to finance app
- Click "Connect Bank Account"
- Plaid Link opens
- Search for bank (use real bank in development/production)
- Enter real credentials
- Complete MFA if required
- Select accounts to connect
- Grant permissions
- Accounts appear in dashboard
- Click "Sync Transactions"
- Verify transactions loaded
Budget Alert Flow:
- Create budget with low threshold (e.g., $50 for groceries)
- Ensure current spending > 90% of budget
- Wait for cron job to run (or manually trigger)
- Check email for budget alert
- Verify alert logged in
budget_alert_history - Verify duplicate alert not sent on next run
Troubleshooting
Issue 1: Subdomain Not Working
Symptom: finance.yourdomain.com shows 404 or redirects to main portfolio
Causes:
- DNS records not configured
- DNS not propagated yet
- Middleware routing not working
Solutions:
Check DNS:
# Check if DNS is configured
nslookup finance.yourdomain.com
# Should return:
# finance.yourdomain.com canonical name = cname.vercel-dns.com
Check Middleware:
- Verify
middleware.tsexists with subdomain routing logic - Check middleware is deployed (review build logs)
- Test locally:
http://finance.localhost:3000should work
Force Redeploy:
- Go to Vercel dashboard
- Click "Redeploy" to trigger new deployment
- Verify middleware is included in build
Issue 2: Environment Variables Not Working
Symptom: API calls fail, Plaid/Supabase errors, or build fails
Causes:
- Environment variables not set in Vercel
- Variables set but not in Production scope
- Typos in variable names
Solutions:
Verify Variables:
- Go to Settings > Environment Variables in Vercel
- Check all required variables exist
- Verify Production checkbox is selected
- Check for typos (e.g.,
NEXT_PUBLIC_SUPABASE_URLnotNEXT_PUBLIC_SUPABASE_URI)
Test Locally:
// Add to any API route temporarily
export async function GET() {
return Response.json({
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL,
plaidEnv: process.env.PLAID_ENV,
// Don't log secrets!
});
}
Redeploy After Changes:
- Environment variable changes require redeployment
- Go to Deployments > Click "..." > "Redeploy"
Issue 3: Plaid Connection Fails in Production
Symptom: Plaid Link opens but connection fails or accounts don't sync
Causes:
- Still using sandbox credentials in production
- Webhook URL not configured
- Access token encryption failing
Solutions:
Verify Environment:
# Check PLAID_ENV in Vercel
PLAID_ENV=development # Should NOT be 'sandbox' in production
Check Webhook:
- Go to Plaid Dashboard > Webhooks
- Verify webhook URL matches:
https://yourdomain.com/api/plaid/webhook - Check webhook logs for errors
Test Access Token Encryption:
- Verify
ENCRYPTION_KEYis 64 hex characters - Generate new key:
openssl rand -hex 32 - Redeploy after updating
Check Logs:
# In Vercel dashboard, check Function Logs
# Look for Plaid API errors:
# - "invalid_access_token" → token encryption issue
# - "item_login_required" → user needs to relink account
Issue 4: Emails Not Sending
Symptom: Users don't receive emails (welcome, alerts, invites)
Causes:
- Resend API key invalid
- Sender email not verified
- Rate limit exceeded
- Emails going to spam
Solutions:
Verify Resend Configuration:
- Go to Resend Dashboard
- Check Logs for delivery status
- Look for errors (bounced, rejected)
Check API Key:
# Verify in Vercel environment variables
RESEND_API_KEY=re_... (should start with 're_')
Verify Sender Domain:
- If using custom domain, verify DNS records in Resend
- Check DKIM, SPF, MX records are configured
- Test with Resend's
onboarding@resend.devtemporarily
Check Rate Limits:
- Free tier: 3,000 emails/month
- Check usage in Resend dashboard
- Upgrade plan if needed
Test Manually:
# Send test email via API
curl -X POST https://api.resend.com/emails \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "onboarding@resend.dev",
"to": "your-email@example.com",
"subject": "Test Email",
"html": "<p>If you receive this, Resend is working!</p>"
}'
Issue 5: Cron Job Not Running
Symptom: Budget alerts not sending daily
Causes:
- Cron job not configured in Vercel
vercel.jsonnot deployed- Cron secret mismatch
- API route error
Solutions:
Verify Cron Configuration:
- Check
vercel.jsonexists in repository - Verify cron schedule syntax
- Redeploy to apply configuration
Check Cron Status:
- Go to Settings > Cron Jobs in Vercel
- Verify job appears
- Check Last Run timestamp
- Review Status (success/error)
Check Logs:
- Go to Deployments > Functions
- Find
/api/cron/budget-alertsexecutions - Review error messages
Test Manually:
curl -X GET https://yourdomain.com/api/cron/budget-alerts \
-H "Authorization: Bearer YOUR_CRON_SECRET"
Issue 6: Build Failures
Symptom: Deployment fails during build
Common Errors:
TypeScript Errors:
Type error: Property 'X' does not exist on type 'Y'
Solution: Fix type definitions, regenerate Supabase types
Missing Environment Variables:
Error: Missing required environment variable: PLAID_CLIENT_ID
Solution: Add missing variables in Vercel settings
Out of Memory:
JavaScript heap out of memory
Solution:
- Increase Node.js memory: Add to
package.json:
{
"scripts": {
"build": "NODE_OPTIONS='--max_old_space_size=4096' next build"
}
}
- Optimize bundle size
- Upgrade Vercel plan for more resources
Rolling Back Deployments
If a deployment causes issues, you can quickly roll back.
Instant Rollback
Via Vercel Dashboard:
- Go to Deployments
- Find last working deployment
- Click "..." > "Promote to Production"
- Confirm rollback
- Previous deployment becomes live instantly
Via Vercel CLI:
# Install Vercel CLI
npm i -g vercel
# List recent deployments
vercel ls
# Promote specific deployment
vercel promote [deployment-url]
Git Rollback
For code-level issues:
# Revert to previous commit
git revert HEAD
git push origin main
# Or reset to specific commit (destructive)
git reset --hard <commit-hash>
git push --force origin main
Vercel auto-deploys after push.
Production Monitoring
Key Metrics to Monitor
Vercel Analytics:
- Page Load Time: Should be < 2 seconds
- Error Rate: Should be < 1%
- Function Execution: Check for timeouts or errors
- Bandwidth Usage: Monitor to avoid overages
Supabase Monitoring:
- Database Size: Free tier has 500 MB limit
- API Requests: Free tier has 50,000/month
- Active Connections: Monitor for connection leaks
- Query Performance: Slow queries need indexes
Plaid Monitoring:
- API Usage: Track against plan limits
- Item Health: Monitor for login_required errors
- Webhook Success: Check delivery rate
Resend Monitoring:
- Email Deliverability: Aim for > 95%
- Bounce Rate: Should be < 5%
- Spam Rate: Should be < 0.1%
- Monthly Usage: Track against plan limit
Setting Up Alerts
Vercel Alerts:
- Go to Settings > Notifications
- Enable alerts for:
- Deployment failures
- Function errors
- Downtime
Supabase Alerts:
- Go to Project Settings > Integrations
- Enable email alerts for:
- Database size approaching limit
- High API usage
Custom Monitoring (optional):
- Set up error tracking (e.g., Sentry)
- Configure uptime monitoring (e.g., UptimeRobot)
- Add analytics (e.g., Google Analytics, Plausible)
Security Checklist
Before going live, verify:
- All environment variables in Vercel (no hardcoded secrets)
- Service role key NOT exposed to client
- Plaid access tokens encrypted before storing
- RLS policies enabled on all Supabase tables
- HTTPS enforced (Vercel does this automatically)
- Cron endpoint secured with
CRON_SECRET - Admin routes protected with
isAdmincheck - CORS configured for API routes (if needed)
- Rate limiting on sensitive endpoints (login, signup)
- Email templates don't expose sensitive data
- Webhook endpoints verify signatures (Plaid)
- SQL injection prevented (using parameterized queries)
- XSS prevented (React escapes by default)
Performance Optimization
Vercel Settings:
- Enable Edge Functions for global low latency (if using)
- Configure Image Optimization for faster loads
- Set up Incremental Static Regeneration (ISR) for static pages
Caching:
- Use SWR for client-side data caching
- Set
Cache-Controlheaders on API routes - Leverage Vercel's CDN for static assets
Database:
- Add indexes to frequently queried columns
- Use Supabase connection pooling
- Optimize queries (select only needed columns)
Bundle Size:
- Use dynamic imports for heavy components
- Tree-shake unused dependencies
- Analyze bundle with
@next/bundle-analyzer
Cost Estimates
Free Tier (MVP):
- Vercel: Free (100 GB bandwidth, 1,000 build minutes/month)
- Supabase: Free (500 MB database, 50,000 API requests/month)
- Plaid Development: Free (100 items, real bank connections)
- Resend: Free (3,000 emails/month)
- Total: $0/month
Pro Tier (Scale):
- Vercel Pro: $20/month (1 TB bandwidth, serverless functions)
- Supabase Pro: $25/month (8 GB database, 500k API requests/month)
- Plaid Production: Variable (per active Item, ~$2-5/item/month depending on products)
- Resend Pro: $20/month (50,000 emails/month)
- Total: ~$65-70/month + Plaid usage
Next Steps After Deployment
- Test All Flows: Run through complete user journeys
- Monitor Logs: Check Vercel and Supabase logs for errors
- Invite Beta Users: Get feedback before public launch
- Set Up Analytics: Track user behavior and conversions
- Document Issues: Keep track of bugs and feature requests
- Plan Iterations: Schedule regular updates and improvements
Summary
Deployment checklist:
- ✅ Supabase production project created and configured
- ✅ Database migrations run successfully
- ✅ RLS policies enabled and verified
- ✅ Plaid development/production credentials configured
- ✅ Webhook URL set up in Plaid dashboard
- ✅ Resend account created with API key
- ✅ Sender email domain verified (optional but recommended)
- ✅ Vercel project connected to GitHub
- ✅ All environment variables configured in Vercel
- ✅ Main domain and subdomains configured with DNS
- ✅ SSL certificates auto-generated and active
- ✅ Cron jobs configured for budget alerts
- ✅ Post-deployment verification completed
- ✅ Monitoring and alerts set up
Your OneLibro application is now live and ready for users! 🎉
For ongoing maintenance, refer to the Testing Guide and troubleshooting sections above.