Skip to main content

Overview

Money Tracker production deployment involves:
  1. Creating a Supabase project
  2. Deploying the database schema and Edge Functions
  3. Configuring production secrets
  4. Deploying the frontend to a hosting provider
  5. Setting up Google Cloud integrations

Prerequisites

Step 1: Create Supabase project

1

Create a new project

  1. Go to Supabase Dashboard
  2. Click “New project”
  3. Choose your organization
  4. Enter project name and database password
  5. Select a region close to your users
  6. Wait for provisioning to complete (2-3 minutes)
2

Link local project to remote

supabase login
supabase link --project-ref your-project-ref
Your project ref is in the Supabase Dashboard URL: https://app.supabase.com/project/[your-project-ref]
3

Push database schema

supabase db push
This runs all migrations from supabase/migrations/ on your remote database.
Production seed files are not automatically run. See Seed production data below if you need initial data.

Step 2: Configure production secrets

Edge Functions secrets

Set all required environment variables for Edge Functions:
supabase secrets set \
  SUPABASE_URL=https://your-project-ref.supabase.co \
  SUPABASE_ANON_KEY=your_anon_key \
  SUPABASE_SERVICE_ROLE_KEY=your_service_role_key \
  FRONTEND_URL=https://your-domain.com \
  GOOGLE_CLIENT_ID=your_google_client_id \
  GOOGLE_CLIENT_SECRET=your_google_client_secret \
  OAUTH_REDIRECT_URI=https://your-domain.com/auth/callback \
  GOOGLE_PROJECT_ID=your_google_project_id \
  PUBSUB_TOPIC=gmail-notifications \
  XAI_API_KEY=your_xai_api_key \
  LANGFUSE_SECRET_KEY=your_langfuse_secret_key \
  LANGFUSE_PUBLIC_KEY=your_langfuse_public_key \
  LANGFUSE_BASE_URL=https://cloud.langfuse.com \
  CORS_ALLOWED_ORIGINS=https://your-domain.com \
  CORS_ALLOW_CREDENTIALS=false \
  INTERNAL_FUNCTIONS_SECRET=$(openssl rand -base64 32)
Save the generated INTERNAL_FUNCTIONS_SECRET value. You’ll need it for the Vault secret in the next step.

Vault secret

The renew_gmail_watches() database function requires INTERNAL_FUNCTIONS_SECRET in the Vault:
1

Connect to your database

Use the SQL Editor in Supabase Dashboard or connect via psql:
supabase db connect
2

Create the Vault secret

select vault.create_secret(
  'YOUR_INTERNAL_FUNCTIONS_SECRET_FROM_ABOVE',
  'INTERNAL_FUNCTIONS_SECRET',
  'Internal token used by renew_gmail_watches()'
);
Use the exact same value you set for the Edge Functions secret.
3

Verify the secret

select name, length(decrypted_secret) as secret_len, updated_at
from vault.decrypted_secrets
where name = 'INTERNAL_FUNCTIONS_SECRET';
You should see:
name                        | secret_len | updated_at
INTERNAL_FUNCTIONS_SECRET   | 44         | 2026-03-04 ...
The Vault secret and Edge Functions secret must match exactly, or the Gmail watch renewal cron job will fail.

Step 3: Deploy Edge Functions

Deploy all Edge Functions to production:
supabase functions deploy
To deploy a specific function:
supabase functions deploy auth-callback
supabase functions deploy gmail-webhook
supabase functions deploy process-document
After deploying functions, verify they’re running in the Supabase Dashboard under Edge Functions.

Step 4: Configure Google Cloud

OAuth credentials

1

Create OAuth consent screen

  1. Go to Google Cloud Console
  2. Navigate to APIs & Services > OAuth consent screen
  3. Select “External” user type
  4. Fill in app name, user support email, and developer contact
  5. Add scopes:
    • https://www.googleapis.com/auth/gmail.readonly
    • https://www.googleapis.com/auth/gmail.modify
  6. Add test users if in testing mode
2

Create OAuth credentials

  1. Go to APIs & Services > Credentials
  2. Click “Create Credentials” > “OAuth client ID”
  3. Choose “Web application”
  4. Add authorized redirect URIs:
    • https://your-domain.com/auth/callback
  5. Copy Client ID and Client Secret to your Edge Functions secrets
3

Enable Gmail API

  1. Go to APIs & Services > Library
  2. Search for “Gmail API”
  3. Click “Enable”

Pub/Sub configuration

1

Create Pub/Sub topic

gcloud pubsub topics create gmail-notifications
2

Create push subscription

gcloud pubsub subscriptions create gmail-notifications-sub \
  --topic=gmail-notifications \
  --push-endpoint=https://your-project-ref.supabase.co/functions/v1/gmail-webhook
3

Grant Gmail publish permissions

gcloud pubsub topics add-iam-policy-binding gmail-notifications \
  --member=serviceAccount:[email protected] \
  --role=roles/pubsub.publisher
Test the webhook endpoint manually to ensure it’s reachable:
curl https://your-project-ref.supabase.co/functions/v1/health

Step 5: Deploy frontend

The frontend is a static React application built with Vite. You can deploy it to any static hosting provider.

Option 1: Vercel

1

Install Vercel CLI

npm i -g vercel
2

Configure environment variables

Create a .env.production file or set via Vercel Dashboard:
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_ANON_KEY=your_anon_key
3

Deploy

cd packages/frontend
vercel --prod

Option 2: Railway

1

Connect your repository

  1. Go to Railway
  2. Click “New Project” > “Deploy from GitHub repo”
  3. Select your repository
2

Configure build settings

  • Build Command: cd packages/frontend && bun install && bun run build
  • Start Command: bun run preview
  • Root Directory: /
3

Set environment variables

Add in Railway Dashboard:
  • SUPABASE_URL
  • SUPABASE_ANON_KEY

Option 3: Custom server

Build and serve the frontend manually:
cd packages/frontend
bun install --production
bun run build
Serve the dist/ folder with any static file server:
# Using serve
npx serve dist

# Using nginx
sudo cp -r dist/* /var/www/html/

# Using caddy
caddy file-server --root dist
Ensure your web server is configured for single-page applications. All routes should serve index.html for client-side routing to work.

Step 6: Update CORS and redirect URIs

Update your production URLs in:
  1. Edge Functions secrets:
    supabase secrets set CORS_ALLOWED_ORIGINS=https://your-domain.com
    supabase secrets set FRONTEND_URL=https://your-domain.com
    supabase secrets set OAUTH_REDIRECT_URI=https://your-domain.com/auth/callback
    
  2. Google Cloud Console:
    • Update authorized redirect URIs in OAuth credentials
    • Update authorized domains in OAuth consent screen
  3. Supabase Dashboard:
    • Go to Authentication > URL Configuration
    • Add https://your-domain.com to “Site URL”
    • Add https://your-domain.com/auth/callback to “Redirect URLs”

Seed production data

Only run seed scripts on production if you need initial test data. Skip this step for real user deployments.
To manually run seed scripts on production:
supabase db execute --file supabase/seeds/001_auth_test_user.sql
supabase db execute --file supabase/seeds/002_transactions_test_user.sql
Or connect to the database and run SQL directly:
supabase db connect
\i supabase/seeds/001_auth_test_user.sql

Post-deployment checklist

1

Verify Edge Functions

Test each endpoint:
curl https://your-project-ref.supabase.co/functions/v1/health
Expected response: {"status":"ok"}
2

Test OAuth flow

  1. Visit your frontend at https://your-domain.com
  2. Sign up for a new account
  3. Connect Gmail
  4. Verify OAuth redirect works correctly
3

Verify Gmail webhook

  1. Connect Gmail in the app
  2. Check that a watch was created: view gmail_connections table
  3. Send a test transaction email to your Gmail
  4. Verify it appears in the app within 30 seconds
4

Check cron job logs

The renew_gmail_watches() function runs every day at 2 AM UTC.View logs in Supabase Dashboard under Database > Extensions > pg_cron:
select * from cron.job_run_details
order by start_time desc
limit 10;
5

Monitor errors

  • Check Edge Functions logs in Supabase Dashboard
  • Enable error tracking (Sentry, LogRocket, etc.)
  • Set up uptime monitoring (UptimeRobot, Pingdom, etc.)

Monitoring and maintenance

Health checks

The /health endpoint provides a simple health check:
curl https://your-project-ref.supabase.co/functions/v1/health

Gmail watch renewal

Gmail watches expire after 7 days. The cron job automatically renews them:
select cron.schedule(
  'renew-gmail-watches',
  '0 2 * * *',  -- Daily at 2 AM UTC
  $$
  select renew_gmail_watches();
  $$
);
Monitor renewal status:
select email, watch_expiration
from gmail_connections
where watch_expiration < now() + interval '24 hours'
order by watch_expiration;
If watch renewal fails, users will stop receiving real-time email updates. Set up alerts for watches expiring within 24 hours.

Database backups

Supabase automatically backs up your database daily. To create manual backups:
supabase db dump -f backup.sql
Restore from a backup:
supabase db execute --file backup.sql

Scaling considerations

  • Database: Supabase Pro plan includes connection pooling and read replicas
  • Edge Functions: Automatically scale with Deno Deploy
  • Gmail API: Subject to quota limits
  • Pub/Sub: Monitor message delivery latency in Google Cloud Console

Troubleshooting

  1. Verify redirect URI matches exactly in Google Cloud Console
  2. Check CORS_ALLOWED_ORIGINS and FRONTEND_URL secrets
  3. Ensure OAuth consent screen is published (not in testing mode)
  1. Check Pub/Sub subscription is active: gcloud pubsub subscriptions describe gmail-notifications-sub
  2. Verify IAM permissions for [email protected]
  3. Test webhook endpoint manually
  4. Check watch expiration in gmail_connections table
  1. Verify INTERNAL_FUNCTIONS_SECRET matches in both Edge Functions secrets and Vault
  2. Check cron logs: select * from cron.job_run_details
  3. Manually trigger: select renew_gmail_watches();
  1. Enable connection pooling in Supabase Dashboard
  2. Use supavisor for transaction pooling
  3. Audit Edge Functions for connection leaks
  1. Check function execution time in logs
  2. Optimize database queries (add indexes, reduce joins)
  3. Increase timeout in function config (max 120s)

Rolling back

If you need to roll back a deployment:

Roll back Edge Functions

# List recent deployments
supabase functions list

# Deploy a specific version
supabase functions deploy function-name --version VERSION_ID

Roll back database migrations

Rolling back migrations can cause data loss. Always backup first.
# Create backup
supabase db dump -f backup-before-rollback.sql

# Roll back to a specific migration
supabase migration repair --status reverted --version TIMESTAMP

Security recommendations

Cost optimization

  • Use Supabase connection pooling to reduce database connections
  • Implement edge caching for static assets
  • Optimize database queries and add appropriate indexes
  • Monitor Edge Function invocations and optimize frequently called functions
  • Set up Google Cloud budget alerts
  • Consider Supabase Pro plan for better performance and support

Build docs developers (and LLMs) love