Deployment

Guide for deploying ShipKit applications to production using Vercel, managing environments, and setting up CI/CD

Deployment

This guide covers deploying ShipKit applications to production environments, with a focus on Vercel deployment, environment configuration, database migrations, and continuous integration/deployment (CI/CD).

Vercel Deployment

Initial Setup

  1. Connect Repository

    • Log in to Vercel
    • Import your Git repository
    • Select the project root directory
  2. Configure Project

    # Install Vercel CLI
    pnpm add -g vercel
    
    # Login to Vercel
    vercel login
    
    # Link local project to Vercel
    vercel link
    
  3. Build Settings

    {
      "buildCommand": "pnpm build",
      "devCommand": "pnpm dev",
      "installCommand": "pnpm install --no-frozen-lockfile",
      "framework": "nextjs"
    }
    

Environment Configuration

  1. Production Environment

    # .env.production
    NEXT_PUBLIC_APP_URL="https://your-domain.com"
    NEXT_PUBLIC_APP_NAME="ShipKit"
    DATABASE_URL="postgresql://user:pass@host:5432/db"
    NEXTAUTH_URL="https://your-domain.com"
    NEXTAUTH_SECRET="your-secret"
    OPENAI_API_KEY="your-key"
    RESEND_API_KEY="your-key"
    STRIPE_SECRET_KEY="your-key"
    STRIPE_WEBHOOK_SECRET="your-secret"
    
  2. Preview Environment

    # .env.preview
    NEXT_PUBLIC_APP_URL="https://preview.your-domain.com"
    NEXT_PUBLIC_APP_NAME="ShipKit Preview"
    DATABASE_URL="postgresql://user:pass@host:5432/preview_db"
    NEXTAUTH_URL="https://preview.your-domain.com"
    NEXTAUTH_SECRET="preview-secret"
    OPENAI_API_KEY="preview-key"
    RESEND_API_KEY="preview-key"
    STRIPE_SECRET_KEY="preview-key"
    STRIPE_WEBHOOK_SECRET="preview-secret"
    

Domain Configuration

  1. Custom Domain Setup

    # Add custom domain
    vercel domains add your-domain.com
    
    # Configure DNS
    vercel dns add your-domain.com @ A 76.76.21.21
    vercel dns add your-domain.com www CNAME cname.vercel-dns.com
    
  2. SSL Configuration

    • Vercel automatically provisions SSL certificates
    • Supports automatic renewal
    • Enables HTTPS by default

Database Migrations

Production Deployment

# Generate migration
pnpm prisma migrate dev --name init

# Deploy migration
pnpm prisma migrate deploy

# Verify deployment
pnpm prisma migrate status

Rollback Strategy

// prisma/migrations/rollback.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function rollback() {
  try {
    await prisma.$executeRawUnsafe('ROLLBACK;')
    console.log('Rollback successful')
  } catch (error) {
    console.error('Rollback failed:', error)
  } finally {
    await prisma.$disconnect()
  }
}

rollback()

CI/CD Pipeline

GitHub Actions

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm test
      - run: pnpm test:e2e

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm lint

  deploy:
    needs: [test, lint]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm build
      - uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'

Deployment Checks

# .github/workflows/deployment-check.yml
name: Deployment Check

on:
  deployment_status:

jobs:
  e2e:
    if: github.event.deployment_status.state == 'success'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'
      - run: pnpm install
      - name: Run E2E Tests
        run: pnpm test:e2e
        env:
          PLAYWRIGHT_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }}

Monitoring

Application Monitoring

  1. Error Tracking

    // src/utils/error-tracking.ts
    import * as Sentry from '@sentry/nextjs'
    
    export function initErrorTracking() {
      Sentry.init({
        dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
        environment: process.env.NEXT_PUBLIC_APP_ENV,
        tracesSampleRate: 1.0,
      })
    }
    
  2. Performance Monitoring

    // src/utils/analytics.ts
    import { Analytics } from '@vercel/analytics/react'
    
    export function AnalyticsProvider({ children }) {
      return (
        <>
          {children}
          <Analytics />
        </>
      )
    }
    

Health Checks

// src/app/api/health/route.ts
import { NextResponse } from 'next/server'
import { db } from '@/server/db'

export async function GET() {
  try {
    // Check database connection
    await db.$queryRaw`SELECT 1`

    return NextResponse.json(
      { status: 'healthy', timestamp: new Date().toISOString() },
      { status: 200 }
    )
  } catch (error) {
    return NextResponse.json(
      { status: 'unhealthy', error: error.message },
      { status: 500 }
    )
  }
}

Security

Headers Configuration

// next.config.mjs
const securityHeaders = [
  {
    key: 'X-DNS-Prefetch-Control',
    value: 'on'
  },
  {
    key: 'Strict-Transport-Security',
    value: 'max-age=63072000; includeSubDomains; preload'
  },
  {
    key: 'X-Frame-Options',
    value: 'SAMEORIGIN'
  },
  {
    key: 'X-Content-Type-Options',
    value: 'nosniff'
  },
  {
    key: 'Referrer-Policy',
    value: 'origin-when-cross-origin'
  }
]

export default {
  headers: async () => [
    {
      source: '/:path*',
      headers: securityHeaders,
    },
  ],
}

Environment Variables

// src/env.mjs
import { createEnv } from '@t3-oss/env-nextjs'
import { z } from 'zod'

export const env = createEnv({
  server: {
    DATABASE_URL: z.string().url(),
    NEXTAUTH_URL: z.string().url(),
    NEXTAUTH_SECRET: z.string().min(32),
    OPENAI_API_KEY: z.string().min(32),
    RESEND_API_KEY: z.string().min(32),
    STRIPE_SECRET_KEY: z.string().min(32),
    STRIPE_WEBHOOK_SECRET: z.string().min(32),
  },
  client: {
    NEXT_PUBLIC_APP_URL: z.string().url(),
    NEXT_PUBLIC_APP_NAME: z.string(),
  },
  runtimeEnv: process.env,
})

Deployment Checklist

  1. Pre-deployment

    • [ ] Run all tests (pnpm test)
    • [ ] Check build output (pnpm build)
    • [ ] Verify environment variables
    • [ ] Review security headers
    • [ ] Check database migrations
  2. Deployment

    • [ ] Deploy database migrations
    • [ ] Deploy application
    • [ ] Verify SSL certificates
    • [ ] Check custom domain configuration
  3. Post-deployment

    • [ ] Run health checks
    • [ ] Verify monitoring setup
    • [ ] Test critical user flows
    • [ ] Check error tracking
    • [ ] Monitor performance metrics
  4. Documentation

    • [ ] Update API documentation
    • [ ] Document deployment changes
    • [ ] Update environment variables
    • [ ] Review security measures