Production Deployment
Deploy OpsDash to Vercel (recommended), Docker, or any Next.js-compatible host.
Step 1 — Supabase Cloud
- Go to supabase.com/dashboard → New project
- Choose an organization, set name, database password, and region
- Wait for provisioning (~30 seconds)
Copy API keys
Settings → API → copy:
- Project URL —
https://xxxxxxxxxxxx.supabase.co - anon public key
- service_role key (click “Reveal”)
Push migrations
npx supabase login
npx supabase link --project-ref <your-project-ref>
npx supabase db pushproject-ref is the subdomain portion of your Supabase URL — not the full URL.
Auth redirect URLs (required)
In Supabase Dashboard → Authentication → URL Configuration → Redirect URLs, add:
https://yourdomain.com/api/auth/callback
https://yourdomain.com/api/auth/confirm
http://localhost:3000/api/auth/callback
http://localhost:3000/api/auth/confirmStep 2A — Vercel (Recommended)
git push origin main
# Import at https://vercel.com/new
# → Connect GitHub repo
# → Set all environment variables (see below)
# → DeployAfter deploying:
- Update
NEXT_PUBLIC_APP_URLto your production domain - Update Stripe webhook URL to
https://yourdomain.com/api/webhooks/stripe - Update OAuth redirect URIs in Google/GitHub to your production Supabase URL
Vercel Cron configuration
Add to vercel.json at the project root:
{
"crons": [
{ "path": "/api/cron/due-date-notifications", "schedule": "0 8 * * *" },
{ "path": "/api/cron/deals-at-risk-notifications", "schedule": "0 8 * * *" },
{ "path": "/api/cron/sla-breach-notifications", "schedule": "0 * * * *" },
{ "path": "/api/cron/digest-emails", "schedule": "0 * * * *" },
{ "path": "/api/cron/activity-reminders", "schedule": "*/15 * * * *" }
]
}Vercel adds Authorization: Bearer {CRON_SECRET} automatically when CRON_SECRET is set.
Step 2B — Docker (Private Instance)
# On the target server
git clone <your-repo-url> /opt/opsdash
cd /opt/opsdash
# Create production env file
cp ".env copy.example" .env
# Fill in Supabase Cloud credentials, app URL, Stripe keys
# Build and start
docker-compose up -d --buildThe docker-compose.yml runs the Next.js app on port 3000. Put Nginx or Caddy in front for SSL and domain routing.
Nginx reverse proxy
server {
listen 443 ssl;
server_name ops.clientname.com;
ssl_certificate /etc/letsencrypt/live/ops.clientname.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ops.clientname.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Get SSL with Certbot
# certbot --nginx -d ops.clientname.comSee the full Private Instance Deployment Guide for client-specific setup steps.
Production Checklist
Common Issues
“Organization not found” after signup
The handle_new_user() trigger may not have fired. Verify via Supabase Studio:
SELECT * FROM public.users WHERE id = '<your-user-uuid>';
SELECT * FROM public.organizations WHERE slug LIKE '%your-name%';If empty: supabase db reset re-applies all migrations.
Stripe webhook returning 400
STRIPE_WEBHOOK_SECRET must match the signing secret from the webhook endpoint in Stripe Dashboard — not the publishable key.
Build errors after cloning
pnpm install
pnpm buildRequires pnpm (not npm or yarn) and Node.js ≥ 18.