StarterApp Docs
Deployment

Deploy to Vercel

Production deployment with automatic scaling and preview environments

Vercel provides native Next.js 15 support with automatic edge optimization, preview deployments on every PR, and zero-downtime production rollouts.

Pro Tip

Vercel auto-detects monorepos. Create separate projects for apps/marketing and apps/dashboard with Turbo build commands.

Prerequisites

GitHub Repository

Push your StarterApp to GitHub. Vercel integrates via OAuth, detecting monorepo structure automatically.

Vercel Account

Sign up at vercel.com. The free Hobby tier supports:

  • Unlimited deployments
  • 100GB bandwidth/month
  • Automatic HTTPS
  • Global CDN distribution

Environment Variables Ready

Prepare production values for authentication, database, and billing credentials.

Deployment Walkthrough

Import Repository

Navigate to vercel.com/new and select Import Git Repository.

Monorepo Detection

Vercel auto-detects monorepos. You'll need to create separate projects for apps/marketing and apps/dashboard.

Click Select on your GitHub repository.

Configure Build Settings

# Framework Preset
Next.js

# Root Directory
apps/marketing

# Build Command (override default)
cd ../.. && pnpm turbo run build --filter=marketing

# Install Command (override default)
pnpm install --frozen-lockfile --prod=false

# Output Directory
.next (auto-detected)

# Node.js Version
20.x (from .nvmrc)

Why --prod=false?

Vercel runs builds with NODE_ENV=production. The --prod=false flag ensures devDependencies like TypeScript, Tailwind CSS, and Turbo remain available during compilation.

# Framework Preset
Next.js

# Root Directory
apps/dashboard

# Build Command (override default)
cd ../.. && pnpm turbo run build --filter=dashboard

# Install Command (override default)
pnpm install --frozen-lockfile --prod=false

# Output Directory
.next (auto-detected)

# Node.js Version
20.x (from .nvmrc)

Set Environment Variables

In Configure Project → Environment Variables, add:

# Core Application (update after first deploy)
APP_BASE_URL=https://your-production-domain.com
DASHBOARD_BASE_URL=https://dashboard.your-domain.com
NEXT_PUBLIC_DASHBOARD_BASE_URL=https://dashboard.your-domain.com

# Authentication
BETTER_AUTH_SECRET=<generate: openssl rand -base64 32>
GOOGLE_CLIENT_ID=<from Google Cloud Console>
GOOGLE_CLIENT_SECRET=<from Google Cloud Console>

# Database
NEXT_PUBLIC_CONVEX_URL=https://your-prod.convex.cloud
CONVEX_DEPLOYMENT=production

# Billing
AUTUMN_SECRET_KEY=am_sk_live_<your-production-key>

# Security
ALLOWED_WEB_ORIGINS=https://your-domain.com,https://www.your-domain.com
ENABLE_HSTS=1
HSTS_PRELOAD=0

# Analytics (optional)
NEXT_PUBLIC_POSTHOG_KEY=phc_<your-key>
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
# Use staging/preview credentials
APP_BASE_URL=https://preview-<branch>-<project>.vercel.app
NEXT_PUBLIC_CONVEX_URL=https://your-preview.convex.cloud
CONVEX_DEPLOYMENT=preview

# Same auth secret, different OAuth callbacks
BETTER_AUTH_SECRET=<same-as-production>

# Test billing key
AUTUMN_SECRET_KEY=am_sk_test_<your-test-key>

# Relaxed security for previews
ENABLE_HSTS=0
HSTS_PRELOAD=0
# Local development values
APP_BASE_URL=http://localhost:3000
DASHBOARD_BASE_URL=http://localhost:3001
NEXT_PUBLIC_CONVEX_URL=https://your-dev.convex.cloud

# Development mode disables certain security headers
ENABLE_HSTS=0

Environment Scope

Select appropriate scopes:

  • Production: Only main branch deploys
  • Preview: All branch deploys
  • Development: Local vercel dev command

Deploy

Click Deploy to trigger the initial build. Vercel will:

  1. Clone repository
  2. Install dependencies via pnpm
  3. Run Turbo build with caching
  4. Deploy to global edge network
  5. Provision SSL certificate
  6. Assign production URL

Initial deploy time: ~3-5 minutes Subsequent deploys: ~1-2 minutes (with Turbo cache)

Update Environment URLs

After deployment completes, Vercel assigns a URL:

https://<project-name>.vercel.app

Navigate to Settings → Environment Variables and update:

APP_BASE_URL=https://<project-name>.vercel.app
ALLOWED_WEB_ORIGINS=https://<project-name>.vercel.app

Trigger a redeploy via Deployments → ••• → Redeploy.

Sync Convex Environment

Mirror credentials to Convex so BetterAuth and UseAutumn run in the hosted runtime:

# Set production deployment
npx convex deploy --prod

# Configure environment variables
npx convex env set APP_BASE_URL https://<project-name>.vercel.app --prod
npx convex env set DASHBOARD_BASE_URL https://dashboard-<project>.vercel.app --prod
npx convex env set ALLOWED_WEB_ORIGINS https://<project-name>.vercel.app --prod
npx convex env set BETTER_AUTH_SECRET <same-value-as-vercel> --prod
npx convex env set GOOGLE_CLIENT_ID <same-value-as-vercel> --prod
npx convex env set GOOGLE_CLIENT_SECRET <same-value-as-vercel> --prod
npx convex env set AUTUMN_SECRET_KEY <same-value-as-vercel> --prod

Environment Parity Critical

Mismatched environment variables between Vercel and Convex cause authentication failures and billing errors.

Configure OAuth Callbacks

Update OAuth provider redirect URIs with production domain.

Custom Domain Setup

Add Domain in Vercel

Navigate to Settings → Domains and add your custom domain:

your-domain.com
www.your-domain.com

Vercel provides DNS configuration instructions.

Configure DNS

Add records at your DNS provider:

Type: A
Name: @
Value: 76.76.21.21
TTL: 3600

For www subdomain:

Type: CNAME
Name: www
Value: cname.vercel-dns.com
TTL: 3600

For subdomains (dashboard, app, etc.):

Type: CNAME
Name: dashboard
Value: cname.vercel-dns.com
TTL: 3600

Update Environment Variables

After domain verification, update all environment URLs:

APP_BASE_URL=https://your-domain.com
DASHBOARD_BASE_URL=https://dashboard.your-domain.com
ALLOWED_WEB_ORIGINS=https://your-domain.com,https://www.your-domain.com

Update OAuth callbacks with new domain and redeploy.

Preview Deployments

Every pull request receives an automatic preview deployment.

Pro Tip

Preview URLs follow the pattern: https://<project>-git-<branch>-<team>.vercel.app

Preview Configuration

// vercel.json (optional)
{
  "git": {
    "deploymentEnabled": {
      "main": true,
      "staging": true
    }
  },
  "github": {
    "autoAlias": true,
    "silent": false
  }
}

Preview Environment Variables

Previews inherit environment variables based on scope. Override preview-specific values in Settings → Environment Variables → Preview.

Performance Optimization

Vercel Edge Runtime provides global distribution with minimal latency:

// app/api/auth/[...all]/route.ts
export const runtime = 'edge'; // Deploy to 100+ global regions
export const preferredRegion = 'auto'; // Closest to user

Compatible with:

  • Authentication routes (BetterAuth)
  • API endpoints (server actions via edge compatibility)
  • Middleware

Not compatible with:

  • Node.js-specific APIs (fs, child_process)
  • Large dependencies (>1MB after minification)

Vercel automatically optimizes images via Next.js Image component:

import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero image"
  width={1200}
  height={630}
  priority // LCP optimization
/>

Benefits:

  • Automatic WebP/AVIF conversion
  • Responsive srcset generation
  • Lazy loading by default
  • CDN caching

Configure caching headers for optimal performance:

// next.config.ts
export default {
  async headers() {
    return [
      {
        source: '/api/public/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, s-maxage=3600, stale-while-revalidate=86400'
          }
        ]
      }
    ];
  }
};

Vercel respects Cache-Control headers for edge caching.

Monitoring and Analytics

Troubleshooting

Advanced Configuration

{
  "buildCommand": "pnpm turbo run build --filter=marketing",
  "installCommand": "pnpm install --frozen-lockfile --prod=false",
  "framework": "nextjs",
  "regions": ["iad1"],
  "env": {
    "APP_NAME": "StarterApp"
  }
}
// next.config.ts
export default {
  async redirects() {
    return [
      {
        source: '/old-path',
        destination: '/new-path',
        permanent: true
      }
    ];
  }
};

Security headers are configured in middleware.ts but can be supplemented:

// next.config.ts
export default {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'X-Frame-Options',
            value: 'DENY'
          }
        ]
      }
    ];
  }
};