Deployment & infrastructure
Database, email, billing, and hosting setup
After scaffolding, you need to set up external services. You can do this manually, via coldstart setup, or conversationally with Claude Code and MCP servers.
Recommended order: Database first, then auth secrets, then billing, then email, then hosting. Each step depends on the previous one — billing needs auth, email needs a verified domain, and hosting needs all env vars set.
Interactive setup
coldstart setupWalks you through each service based on your project's .coldstart.json. Every step is optional and shows its status at the end.
Local development
Generated projects use portless for stable HTTPS *.localhost URLs during development:
pnpm dev # starts portless proxy + all apps| App | URL |
|---|---|
| Web | https://web.<name>.localhost |
| API | https://api.<name>.localhost |
| Mobile | Runs natively on device/simulator (no portless) |
Portless auto-starts on first pnpm dev run and provides valid HTTPS certificates for localhost.
If you prefer plain HTTP with classic ports, use PORTLESS=0 pnpm dev or pnpm dev:no-tls. Web runs on http://localhost:3000, API on http://localhost:3001.
Service by service
Database (Neon)
> Create a Neon database for my projectClaude uses the Neon MCP to create a project, gets the connection string, and writes it to apps/api/.env.
- Create a project at console.neon.tech
- Copy the connection string
- Add to
apps/api/.env:
DATABASE_URL=postgresql://user:pass@ep-xxx.neon.tech/dbname?sslmode=require- Run migrations:
pnpm -F @my-app/db db:generate
pnpm -F @my-app/db db:migrateAuth secrets
Generate a secret (min 32 characters) and configure the API URL:
BETTER_AUTH_SECRET=your-random-secret-at-least-32-characters
BETTER_AUTH_URL=https://api.my-app.localhost
CORS_ORIGIN=https://web.my-app.localhostThese URLs use portless (pnpm dev). If using plain HTTP (pnpm dev:no-tls), use http://localhost:3001 and http://localhost:3000 instead.
For OAuth providers, create apps on each provider's console and add the credentials.
Billing
- Create products and prices in Stripe Dashboard
- Configure webhook: Developers > Webhooks > Add endpoint
- URL:
https://your-api.com/webhooks/stripe - Events:
customer.subscription.*,checkout.session.completed
- URL:
- Add to
apps/api/.env:
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...- Add to
apps/web/.env:
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...- Create products on dashboard.polar.sh
- Configure webhook URL:
https://your-api.com/webhooks/polar - Add to
apps/api/.env:
POLAR_ACCESS_TOKEN=...
POLAR_WEBHOOK_SECRET=...For mobile billing, configure RevenueCat: create products matching your App Store/Play Store offerings, add API keys to your mobile .env.
Email (Resend)
> Set up Resend for my projectClaude uses the Resend MCP to configure a sending domain and add the API key.
- Create an account at resend.com
- Add and verify your domain
- Add API key to
apps/api/.env:
RESEND_API_KEY=re_...Email templates are in packages/email/src/ — preview them with pnpm -F @my-app/email preview (runs on port 3333).
Web hosting (Vercel)
> Deploy my web app to Vercelpnpm dlx vercelOr connect your Git repo in the Vercel dashboard — auto-deploys on push.
API hosting (Docker)
The generated Dockerfile is a multi-stage build optimized for production:
docker build -t my-api -f apps/api/Dockerfile .
docker run -p 3001:3001 my-apiOr use the docker-compose.yml for local development with PostgreSQL:
docker compose up -d # starts Postgres on port 5432Mobile builds (EAS)
cd apps/mobile
pnpm dlx eas-cli init --non-interactiveThen use the release scripts:
bash scripts/release-local.sh— full pipeline: version bump, EAS build, submit, TestFlightbash scripts/release.sh— CI-friendly version bump
The Expo MCP server is configured for Claude Code to manage builds and submissions.
Environment checklist
Before going live, verify all required environment variables are set. Missing any will cause runtime errors.
Always required:
| Variable | Description |
|---|---|
DATABASE_URL | Neon PostgreSQL connection string |
BETTER_AUTH_SECRET | Random string, min 32 characters |
BETTER_AUTH_URL | Public URL of your API (e.g. https://api.myapp.com) |
CORS_ORIGIN | Public URL of your web app (e.g. https://myapp.com) |
If using email (Resend):
| Variable | Description |
|---|---|
RESEND_API_KEY | Resend API key (re_...) |
If using OAuth:
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | Google OAuth credentials |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | GitHub OAuth credentials |
APPLE_CLIENT_ID / APPLE_CLIENT_SECRET | Apple OAuth credentials |
If using Stripe:
| Variable | Description |
|---|---|
STRIPE_SECRET_KEY | Stripe secret key (sk_live_...) |
STRIPE_WEBHOOK_SECRET | Stripe webhook signing secret (whsec_...) |
If using Polar:
| Variable | Description |
|---|---|
POLAR_ACCESS_TOKEN | Polar API access token |
POLAR_WEBHOOK_SECRET | Polar webhook HMAC secret |
If mobile billing:
| Variable | Description |
|---|---|
REVENUECAT_WEBHOOK_SECRET | RevenueCat webhook Bearer token |
| Variable | Description |
|---|---|
NEXT_PUBLIC_API_URL | Public URL of your API |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe publishable key (if Stripe) |
NEXT_PUBLIC_POSTHOG_KEY | PostHog project API key |
NEXT_PUBLIC_POSTHOG_HOST | PostHog host URL |
| Variable | Description |
|---|---|
EXPO_PUBLIC_API_URL | Public URL of your API |
EXPO_PUBLIC_REVENUECAT_API_KEY | RevenueCat public API key (if billing) |
EXPO_PUBLIC_POSTHOG_KEY | PostHog project API key |
EXPO_PUBLIC_POSTHOG_HOST | PostHog host URL |
CI/CD
Two GitHub Actions workflows are generated:
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml | Push/PR to main | Lint, check-types, test, build |
deploy.yml | Push to main | Vercel deployment placeholder |
desktop.yml | Version tags | Tauri multi-platform builds (if desktop) |
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm check-types
- run: pnpm test
- run: pnpm buildThe deploy.yml workflow is a placeholder — connect your Vercel project via Git integration for automatic deployments, or customize the workflow with pnpm dlx vercel --prod --token $VERCEL_TOKEN.
Production checklist
Before launching, go through this checklist:
Switch to live API keys
Replace all test / sk_test_ / sandbox keys with production keys:
- Stripe:
sk_live_...,pk_live_..., newwhsec_...for production webhook - Polar: production access token and webhook secret
- RevenueCat: production API key (not sandbox)
- Resend: production API key with verified domain
Update webhook URLs
Point all webhook endpoints to your production API:
- Stripe:
https://api.yourdomain.com/webhooks/stripe - Polar:
https://api.yourdomain.com/webhooks/polar - RevenueCat:
https://api.yourdomain.com/webhooks/revenuecat
Configure CORS and auth URLs
Update environment variables to use production domains:
BETTER_AUTH_URL=https://api.yourdomain.com
CORS_ORIGIN=https://yourdomain.comSet up custom domain on Vercel
Add your custom domain in the Vercel dashboard (Settings > Domains). Update NEXT_PUBLIC_API_URL in your web app's Vercel environment variables.
Verify monitoring
PostHog is already configured in generated projects. Verify that:
NEXT_PUBLIC_POSTHOG_KEYandNEXT_PUBLIC_POSTHOG_HOSTare set in production- Events are flowing in app.posthog.com
- Consider setting up error tracking alerts
Run the full build
Before your first production deployment, verify everything builds cleanly:
pnpm lint && pnpm check-types && pnpm test && pnpm build