The infrastructure upgrade your AI skipped.
Your AI built a working prototype. Now make it survive real users. Database, auth, environment, hosting — every step from demo to production-ready.
Throwaway storage is the #1 killer of prototype-stage apps — your data disappears on every deploy. This section moves you onto a real database.
Vercel, Railway, and Render wipe your app's filesystem on every deploy. If your data lives in a local file (SQLite, a JSON blob, anything on disk), it vanishes. Move to a hosted Postgres: Supabase is the easiest path for most builders, Neon and PlanetScale are solid alternatives.
Every request opens a connection to the database. Without pooling (which reuses connections), you'll run out around 10-20 concurrent users and your app will start rejecting traffic. Supabase and Neon both provide a pooled endpoint — point your `DATABASE_URL` at it.
Supabase Pro includes daily backups out of the box, and Neon handles backups automatically too. On the free tier, dump your database weekly with the command below — or wire it up as a scheduled GitHub Action so it runs without you thinking about it.
A login page is not real security. Production auth means verified emails, secure passwords, and password resets that actually work.
Don't let AI hand-roll your auth system — it gets the security details wrong. Use a proven provider: Supabase Auth (free, pairs perfectly with Supabase), Clerk (polished UI, fastest setup), or Auth.js (open source, the default for Next.js). They handle password hashing, sessions, email verification, and password resets correctly so you don't have to.
Without email confirmation, bots will flood you with fake accounts within hours of launch. Turn it on in your auth provider settings. For Supabase: Dashboard → Authentication → Settings → Enable email confirmations.
Sign up with a real inbox. Request a password reset. Click the link, pick a new password, and log back in with it. If anything breaks along the way, fix it before launch — someone will need this on day one.
The .env file your AI generated is a liability. Some values are safe to ship to the browser, some must stay server-side, and none of them belong in git.
Open your .env file. For each variable, decide whether it's safe to ship in your client bundle or whether it has to stay server-side. Client-safe: publishable keys, Supabase URL and anon key. Server-only: service role keys, database URLs, webhook secrets, and any key that spends money.
Your .env file holds secrets. It has no business in version control. Check your git history: if .env was ever committed, rotate every secret in it — even if you deleted the file later. Git remembers everything, and public repos are scraped within minutes.
A local .env is not enough. Set every server-side variable in your hosting dashboard so your deployed app actually has them. Vercel → Settings → Environment Variables. Railway → Variables. Render → Environment.
If your app touches OpenAI, Anthropic, Resend, or any pay-per-use API, set a hard spending cap before launch. A runaway bug or an abusive user can turn into a four-figure bill overnight. Start with $20-50 while you find out what real usage looks like.
Your app runs on localhost. It needs to run on a URL that doesn't end in :3000.
For Next.js: Vercel is the path of least resistance, with Netlify and Railway close behind. For plain React or Vite: Vercel, Netlify, or Cloudflare Pages. Connect your GitHub repo, set your environment variables, and the first deploy is minutes away.
Vercel and Netlify spin up a unique preview URL for every pull request. Use them. Test changes on the preview URL before they ever touch production — it catches regressions that localhost misses. Free on every major platform.
A tiny `/api/health` route that confirms your app can reach the database. Uptime monitors (next section) ping this endpoint every minute so you find out your app is down before your users do.
You can't fix what you can't see. Set up error tracking and basic analytics before your first user hits a bug.
Users don't file bug reports — they leave. Sentry catches every crash in your app, server and client, and pings you with a stack trace detailed enough to actually fix the bug. Its free tier is more than enough for most early-stage apps.
You need to know whether anyone is actually using the thing you built. Pick one: PostHog (free, open source, feature-rich), Plausible (lightweight and privacy-first), or Vercel Analytics (zero setup if you're already on Vercel).
Throwaway storage is the #1 killer of prototype-stage apps — your data disappears on every deploy. This section moves you onto a real database.
Vercel, Railway, and Render wipe your app's filesystem on every deploy. If your data lives in a local file (SQLite, a JSON blob, anything on disk), it vanishes. Move to a hosted Postgres: Supabase is the easiest path for most builders, Neon and PlanetScale are solid alternatives.
Users create accounts. They come back tomorrow. Their data is gone. This is the single most common "my app broke in production" failure for AI-built projects.
Every request opens a connection to the database. Without pooling (which reuses connections), you'll run out around 10-20 concurrent users and your app will start rejecting traffic. Supabase and Neon both provide a pooled endpoint — point your `DATABASE_URL` at it.
# Supabase: use the pooler URL (port 6543), not the direct URL (port 5432).
DATABASE_URL="postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres"
# Neon: the default connection string they give you is already pooled.
DATABASE_URL="postgresql://user:pass@ep-cool-name-123.us-east-2.aws.neon.tech/neondb?sslmode=require"Supabase Pro includes daily backups out of the box, and Neon handles backups automatically too. On the free tier, dump your database weekly with the command below — or wire it up as a scheduled GitHub Action so it runs without you thinking about it.
# Export your entire database to a backup file (run weekly)
pg_dump $DATABASE_URL --format=custom --file=backup-$(date +%F).dump
# Restore from a backup if something goes wrong:
pg_restore --dbname=$DATABASE_URL backup-2026-04-10.dumpA login page is not real security. Production auth means verified emails, secure passwords, and password resets that actually work.
Don't let AI hand-roll your auth system — it gets the security details wrong. Use a proven provider: Supabase Auth (free, pairs perfectly with Supabase), Clerk (polished UI, fastest setup), or Auth.js (open source, the default for Next.js). They handle password hashing, sessions, email verification, and password resets correctly so you don't have to.
Custom auth is where most production security bugs live. AI-generated auth routinely stores passwords in plaintext, issues guessable session tokens, or skips email verification entirely.
Without email confirmation, bots will flood you with fake accounts within hours of launch. Turn it on in your auth provider settings. For Supabase: Dashboard → Authentication → Settings → Enable email confirmations.
A checklist tells you what to do. A scan proves you did it. Paste your URL and verify everything in minutes.
Sign up with a real inbox. Request a password reset. Click the link, pick a new password, and log back in with it. If anything breaks along the way, fix it before launch — someone will need this on day one.
The .env file your AI generated is a liability. Some values are safe to ship to the browser, some must stay server-side, and none of them belong in git.
Open your .env file. For each variable, decide whether it's safe to ship in your client bundle or whether it has to stay server-side. Client-safe: publishable keys, Supabase URL and anon key. Server-only: service role keys, database URLs, webhook secrets, and any key that spends money.
Any variable prefixed with NEXT_PUBLIC_ is baked into your JavaScript bundle — anyone can read it by viewing source. That's fine for keys designed to be public; it's catastrophic for your OpenAI key or your database URL.
# ✅ Client-safe (NEXT_PUBLIC_ → bundled into your JavaScript)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... # scoped by RLS — safe to ship
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
# ❌ Server-only (no NEXT_PUBLIC_ prefix)
SUPABASE_SERVICE_ROLE_KEY=eyJ... # bypasses RLS — never expose
STRIPE_SECRET_KEY=sk_live_... # can issue refunds and charges
DATABASE_URL=postgresql://... # direct database access
OPENAI_API_KEY=sk-... # someone will drain itYour .env file holds secrets. It has no business in version control. Check your git history: if .env was ever committed, rotate every secret in it — even if you deleted the file later. Git remembers everything, and public repos are scraped within minutes.
# Make sure .env is gitignored
echo ".env" >> .gitignore
# Has .env ever been committed? Find out:
git log --all --full-history -- .env
# If it was, rotate every secret inside. Assume it's public.A local .env is not enough. Set every server-side variable in your hosting dashboard so your deployed app actually has them. Vercel → Settings → Environment Variables. Railway → Variables. Render → Environment.
If your app touches OpenAI, Anthropic, Resend, or any pay-per-use API, set a hard spending cap before launch. A runaway bug or an abusive user can turn into a four-figure bill overnight. Start with $20-50 while you find out what real usage looks like.
# Set limits in each provider's dashboard:
# OpenAI: Settings → Billing → Usage limits → Hard cap ($20-50)
# Anthropic: Settings → Plans & Billing → Monthly limit
# Supabase: Settings → Billing → Spending cap
# Resend: Settings → Billing → Rate limitsYour app runs on localhost. It needs to run on a URL that doesn't end in :3000.
For Next.js: Vercel is the path of least resistance, with Netlify and Railway close behind. For plain React or Vite: Vercel, Netlify, or Cloudflare Pages. Connect your GitHub repo, set your environment variables, and the first deploy is minutes away.
Vercel and Netlify spin up a unique preview URL for every pull request. Use them. Test changes on the preview URL before they ever touch production — it catches regressions that localhost misses. Free on every major platform.
A tiny `/api/health` route that confirms your app can reach the database. Uptime monitors (next section) ping this endpoint every minute so you find out your app is down before your users do.
// app/api/health/route.ts (Next.js)
import { NextResponse } from 'next/server';
export async function GET() {
try {
// Check database connection
await db.query('SELECT 1');
return NextResponse.json({ status: 'ok' });
} catch {
return NextResponse.json({ status: 'error' }, { status: 503 });
}
}You can't fix what you can't see. Set up error tracking and basic analytics before your first user hits a bug.
Users don't file bug reports — they leave. Sentry catches every crash in your app, server and client, and pings you with a stack trace detailed enough to actually fix the bug. Its free tier is more than enough for most early-stage apps.
# Install
npm install @sentry/nextjs
# Run the wizard — it configures everything for you
npx @sentry/wizard@latest -i nextjs
# Confirm it's working — throw a test error
throw new Error('Sentry test error');You need to know whether anyone is actually using the thing you built. Pick one: PostHog (free, open source, feature-rich), Plausible (lightweight and privacy-first), or Vercel Analytics (zero setup if you're already on Vercel).