> ## Documentation Index
> Fetch the complete documentation index at: https://docs.weventures.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment & DevOps

> Production deployment, monitoring, and optimization with modern DevOps practices

# 🚀 Deployment & DevOps

<Info>
  Deployment strategy should align with scale expectations and team capabilities from discovery
</Info>

## Modern Deployment Stack

<CardGroup cols={3}>
  <Card title="Vercel" icon="triangle" color="#000000">
    Best for Next.js apps with global edge deployment
  </Card>

  <Card title="Railway" icon="train" color="#0B0D0E">
    Full-stack deployment with built-in databases
  </Card>

  <Card title="Cloud Run" icon="cloud" color="#4285F4">
    Serverless containers for ultimate flexibility
  </Card>
</CardGroup>

## Environment Configuration

<Steps>
  <Step title="Environment Setup">
    <Card title="Environment Configuration" icon="gear">
      Set up environments based on discovery requirements
    </Card>

    <CodeGroup>
      ```bash Development theme={null}
      # .env.local
      NEXT_PUBLIC_APP_URL=http://localhost:3000
      NEXT_PUBLIC_SUPABASE_URL=https://[project].supabase.co
      NEXT_PUBLIC_SUPABASE_ANON_KEY=[anon-key]
      SUPABASE_SERVICE_ROLE_KEY=[service-key]

      # Feature flags
      NEXT_PUBLIC_FEATURE_CHAT=true
      NEXT_PUBLIC_FEATURE_ANALYTICS=false

      # Development tools
      NEXT_PUBLIC_DEBUG=true
      NEXT_PUBLIC_API_MOCKING=true
      ```

      ```bash Staging theme={null}
      # .env.staging
      NEXT_PUBLIC_APP_URL=https://staging.example.com
      NEXT_PUBLIC_SUPABASE_URL=https://[project-staging].supabase.co
      NEXT_PUBLIC_SUPABASE_ANON_KEY=[staging-anon-key]

      # Monitoring
      NEXT_PUBLIC_SENTRY_DSN=[staging-dsn]
      SENTRY_ENVIRONMENT=staging
      NEXT_PUBLIC_POSTHOG_KEY=[staging-posthog]

      # Feature flags
      NEXT_PUBLIC_FEATURE_CHAT=true
      NEXT_PUBLIC_FEATURE_ANALYTICS=true
      ```

      ```bash Production theme={null}
      # .env.production
      NEXT_PUBLIC_APP_URL=https://example.com
      NEXT_PUBLIC_SUPABASE_URL=https://[project-prod].supabase.co
      NEXT_PUBLIC_SUPABASE_ANON_KEY=[prod-anon-key]

      # Monitoring & Analytics
      NEXT_PUBLIC_SENTRY_DSN=[prod-dsn]
      SENTRY_ENVIRONMENT=production
      NEXT_PUBLIC_GA_ID=[google-analytics]
      NEXT_PUBLIC_MIXPANEL_TOKEN=[mixpanel]
      NEXT_PUBLIC_POSTHOG_KEY=[prod-posthog]
      NEXT_PUBLIC_HOTJAR_ID=[hotjar]

      # Security
      NEXT_PUBLIC_RATE_LIMIT_ENABLED=true
      NEXT_PUBLIC_CSP_ENABLED=true
      ```
    </CodeGroup>
  </Step>

  <Step title="Secret Management">
    ```typescript theme={null}
    // lib/secrets.ts
    import { SecretManagerServiceClient } from '@google-cloud/secret-manager'

    const client = new SecretManagerServiceClient()

    export async function getSecret(name: string): Promise<string> {
      if (process.env.NODE_ENV === 'development') {
        return process.env[name] || ''
      }

      const [version] = await client.accessSecretVersion({
        name: `projects/${process.env.GCP_PROJECT}/secrets/${name}/versions/latest`,
      })

      return version.payload?.data?.toString() || ''
    }

    // Usage
    const apiKey = await getSecret('STRIPE_SECRET_KEY')
    ```
  </Step>
</Steps>

## CI/CD Pipeline

<Tabs>
  <Tab title="GitHub Actions">
    <Card title="Complete CI/CD Pipeline" icon="github">
      Automated testing, deployment, and monitoring
    </Card>

    ```yaml theme={null}
    # .github/workflows/deploy.yml
    name: Deploy

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

    env:
      NODE_VERSION: '20'
      PNPM_VERSION: '8'

    jobs:
      # Dependency installation cache
      install:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4

          - uses: pnpm/action-setup@v2
            with:
              version: ${{ env.PNPM_VERSION }}

          - uses: actions/setup-node@v4
            with:
              node-version: ${{ env.NODE_VERSION }}
              cache: 'pnpm'

          - name: Cache dependencies
            uses: actions/cache@v3
            id: cache
            with:
              path: |
                ~/.pnpm-store
                node_modules
              key: deps-${{ hashFiles('pnpm-lock.yaml') }}

          - name: Install dependencies
            if: steps.cache.outputs.cache-hit != 'true'
            run: pnpm install --frozen-lockfile

      # Code quality checks
      quality:
        needs: install
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: ./.github/actions/setup

          - name: Lint
            run: pnpm lint

          - name: Type check
            run: pnpm type-check

          - name: Format check
            run: pnpm format:check

          - name: Security audit
            run: pnpm audit --production

      # Testing
      test:
        needs: install
        runs-on: ubuntu-latest
        strategy:
          matrix:
            shard: [1, 2, 3, 4]

        services:
          postgres:
            image: postgres:15
            env:
              POSTGRES_PASSWORD: postgres
            options: >-
              --health-cmd pg_isready
              --health-interval 10s
              --health-timeout 5s
              --health-retries 5
            ports:
              - 5432:5432

        steps:
          - uses: actions/checkout@v4
          - uses: ./.github/actions/setup

          - name: Unit tests
            run: pnpm test:unit --shard=${{ matrix.shard }}/4

          - name: Integration tests
            run: pnpm test:integration
            env:
              DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test

          - name: Upload coverage
            uses: codecov/codecov-action@v3
            with:
              files: ./coverage/lcov.info

      # E2E Testing
      e2e:
        needs: [quality, test]
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: ./.github/actions/setup

          - name: Install Playwright
            run: pnpm exec playwright install --with-deps

          - name: Run E2E tests
            run: pnpm test:e2e
            env:
              PLAYWRIGHT_TEST_BASE_URL: ${{ secrets.STAGING_URL }}

          - uses: actions/upload-artifact@v3
            if: always()
            with:
              name: playwright-report
              path: playwright-report/

      # Build
      build:
        needs: [quality, test]
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: ./.github/actions/setup

          - name: Build application
            run: pnpm build
            env:
              NEXT_PUBLIC_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}

          - name: Upload build artifacts
            uses: actions/upload-artifact@v3
            with:
              name: build-output
              path: .next/

      # Deploy to staging
      deploy-staging:
        needs: [build, e2e]
        if: github.event_name == 'pull_request'
        runs-on: ubuntu-latest
        environment:
          name: staging
          url: https://staging.example.com

        steps:
          - uses: actions/checkout@v4

          - name: Deploy to Vercel Staging
            uses: amondnet/vercel-action@v25
            with:
              vercel-token: ${{ secrets.VERCEL_TOKEN }}
              vercel-org-id: ${{ secrets.ORG_ID }}
              vercel-project-id: ${{ secrets.PROJECT_ID }}
              alias-domains: pr-${{ github.event.pull_request.number }}.example.vercel.app

          - name: Comment PR with preview URL
            uses: actions/github-script@v6
            with:
              script: |
                github.rest.issues.createComment({
                  issue_number: context.issue.number,
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  body: '🚀 Preview deployed to https://pr-${{ github.event.pull_request.number }}.example.vercel.app'
                })

      # Deploy to production
      deploy-production:
        needs: [build, e2e]
        if: github.ref == 'refs/heads/main'
        runs-on: ubuntu-latest
        environment:
          name: production
          url: https://example.com

        steps:
          - uses: actions/checkout@v4

          - name: Deploy to Vercel Production
            uses: amondnet/vercel-action@v25
            with:
              vercel-token: ${{ secrets.VERCEL_TOKEN }}
              vercel-org-id: ${{ secrets.ORG_ID }}
              vercel-project-id: ${{ secrets.PROJECT_ID }}
              vercel-args: '--prod'

          - name: Purge CDN Cache
            run: |
              curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.CF_ZONE }}/purge_cache" \
                -H "Authorization: Bearer ${{ secrets.CF_TOKEN }}" \
                -H "Content-Type: application/json" \
                --data '{"purge_everything":true}'

          - name: Run smoke tests
            run: |
              npm install -g newman
              newman run postman-collection.json \
                --environment production.json \
                --bail

          - name: Create release
            uses: actions/create-release@v1
            with:
              tag_name: v${{ github.run_number }}
              release_name: Release ${{ github.run_number }}
              draft: false
              prerelease: false

          - name: Notify deployment
            uses: 8398a7/action-slack@v3
            with:
              status: ${{ job.status }}
              text: |
                Production deployment ${{ job.status }}
                Commit: ${{ github.sha }}
                Author: ${{ github.actor }}
            if: always()
    ```
  </Tab>

  <Tab title="GitLab CI">
    ```yaml theme={null}
    # .gitlab-ci.yml
    stages:
      - install
      - quality
      - test
      - build
      - deploy

    variables:
      NODE_VERSION: "20"
      PNPM_VERSION: "8"

    cache:
      key: ${CI_COMMIT_REF_SLUG}
      paths:
        - .pnpm-store
        - node_modules

    install:
      stage: install
      script:
        - npm install -g pnpm@${PNPM_VERSION}
        - pnpm config set store-dir .pnpm-store
        - pnpm install --frozen-lockfile

    lint:
      stage: quality
      needs: [install]
      script:
        - pnpm lint
        - pnpm type-check

    test:unit:
      stage: test
      needs: [install]
      script:
        - pnpm test:unit --coverage
      coverage: '/Lines\s*:\s*([0-9.]+)%/'
      artifacts:
        reports:
          coverage_report:
            coverage_format: cobertura
            path: coverage/cobertura-coverage.xml

    test:e2e:
      stage: test
      needs: [install]
      services:
        - postgres:15
      script:
        - pnpm test:e2e
      artifacts:
        when: always
        paths:
          - playwright-report/

    build:
      stage: build
      needs: [lint, test:unit]
      script:
        - pnpm build
      artifacts:
        paths:
          - .next/

    deploy:staging:
      stage: deploy
      needs: [build, test:e2e]
      environment:
        name: staging
        url: https://staging.example.com
      only:
        - develop
      script:
        - vercel deploy --token=$VERCEL_TOKEN

    deploy:production:
      stage: deploy
      needs: [build, test:e2e]
      environment:
        name: production
        url: https://example.com
      only:
        - main
      when: manual
      script:
        - vercel deploy --prod --token=$VERCEL_TOKEN
    ```
  </Tab>
</Tabs>

## Platform Configuration

<Tabs>
  <Tab title="Vercel">
    ```json theme={null}
    // vercel.json
    {
      "framework": "nextjs",
      "buildCommand": "pnpm build",
      "devCommand": "pnpm dev",
      "installCommand": "pnpm install",
      "regions": ["iad1", "sfo1"], // Based on audience location

      "functions": {
        "app/api/*/route.ts": {
          "maxDuration": 10
        },
        "app/api/heavy-task/route.ts": {
          "maxDuration": 60
        },
        "app/api/background/*/route.ts": {
          "maxDuration": 300
        }
      },

      "crons": [
        {
          "path": "/api/cron/daily-report",
          "schedule": "0 2 * * *"
        },
        {
          "path": "/api/cron/cleanup",
          "schedule": "0 */6 * * *"
        }
      ],

      "headers": [
        {
          "source": "/(.*)",
          "headers": [
            {
              "key": "X-Content-Type-Options",
              "value": "nosniff"
            },
            {
              "key": "X-Frame-Options",
              "value": "DENY"
            },
            {
              "key": "X-XSS-Protection",
              "value": "1; mode=block"
            }
          ]
        }
      ],

      "redirects": [
        {
          "source": "/old-path",
          "destination": "/new-path",
          "permanent": true
        }
      ],

      "rewrites": [
        {
          "source": "/api/proxy/:path*",
          "destination": "https://api.external.com/:path*"
        }
      ]
    }
    ```
  </Tab>

  <Tab title="Railway">
    ```toml theme={null}
    # railway.toml
    [build]
    builder = "nixpacks"
    buildCommand = "pnpm build"
    nixpacksPlan = """
      [phases.setup]
      nixPkgs = ['nodejs-20_x', 'pnpm']
    """

    [deploy]
    startCommand = "pnpm start"
    healthcheckPath = "/api/health"
    healthcheckTimeout = 30
    restartPolicyType = "on-failure"
    restartPolicyMaxRetries = 3
    numReplicas = 2
    region = "us-west"

    [[services]]
    name = "web"
    port = 3000
    targetPort = 3000
    protocol = "http"

    [[services]]
    name = "worker"
    command = "pnpm worker"
    numReplicas = 1

    [env]
    NODE_ENV = "production"
    PORT = "3000"

    [[volumes]]
    mount = "/data"
    size = "10Gi"

    [[databases]]
    name = "postgres"
    plugin = "postgresql"

    [[databases]]
    name = "redis"
    plugin = "redis"
    ```
  </Tab>

  <Tab title="Docker & K8s">
    ```dockerfile theme={null}
    # Dockerfile
    FROM node:20-alpine AS base

    # Install pnpm
    RUN corepack enable
    RUN corepack prepare pnpm@latest --activate

    # Install dependencies
    FROM base AS deps
    WORKDIR /app
    COPY package.json pnpm-lock.yaml ./
    RUN pnpm install --frozen-lockfile

    # Build application
    FROM base AS builder
    WORKDIR /app
    COPY --from=deps /app/node_modules ./node_modules
    COPY . .

    # Build args for environment
    ARG NEXT_PUBLIC_APP_URL
    ARG NEXT_PUBLIC_SUPABASE_URL
    ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
    ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL

    RUN pnpm build

    # Production image
    FROM base AS runner
    WORKDIR /app
    ENV NODE_ENV=production

    # Add non-root user
    RUN addgroup -g 1001 nodejs
    RUN adduser -S nextjs -u 1001

    # Copy built application
    COPY --from=builder --chown=nextjs:nodejs /app/public ./public
    COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
    COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

    USER nextjs

    EXPOSE 3000
    ENV PORT=3000

    # Health check
    HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
      CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {r.statusCode === 200 ? process.exit(0) : process.exit(1)})"

    CMD ["node", "server.js"]
    ```

    ```yaml theme={null}
    # kubernetes/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: app-deployment
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nextjs-app
      template:
        metadata:
          labels:
            app: nextjs-app
        spec:
          containers:
          - name: app
            image: gcr.io/project/app:latest
            ports:
            - containerPort: 3000
            env:
            - name: NODE_ENV
              value: "production"
            resources:
              requests:
                memory: "256Mi"
                cpu: "250m"
              limits:
                memory: "512Mi"
                cpu: "500m"
            livenessProbe:
              httpGet:
                path: /api/health
                port: 3000
              initialDelaySeconds: 30
              periodSeconds: 10
            readinessProbe:
              httpGet:
                path: /api/health
                port: 3000
              initialDelaySeconds: 5
              periodSeconds: 5
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: app-service
    spec:
      selector:
        app: nextjs-app
      ports:
      - port: 80
        targetPort: 3000
      type: LoadBalancer
    ```
  </Tab>
</Tabs>

## Monitoring & Observability

<Steps>
  <Step title="Error Tracking">
    ```typescript theme={null}
    // sentry.client.config.ts
    import * as Sentry from '@sentry/nextjs'

    Sentry.init({
      dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
      environment: process.env.NEXT_PUBLIC_ENV,

      // Performance monitoring
      tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,

      // Session replay
      replaysSessionSampleRate: 0.1,
      replaysOnErrorSampleRate: 1.0,

      // From discovery requirements
      beforeSend(event, hint) {
        // Filter sensitive data
        if (event.request?.cookies) {
          delete event.request.cookies
        }
        if (event.user?.email) {
          event.user.email = '[REDACTED]'
        }
        return event
      },

      integrations: [
        new Sentry.BrowserTracing({
          tracingOrigins: ['localhost', 'example.com', /^\//],
        }),
        new Sentry.Replay({
          maskAllText: true,
          blockAllMedia: true,
          maskAllInputs: true,
        }),
        new Sentry.ProfilerIntegration(),
      ],

      // Ignore certain errors
      ignoreErrors: [
        'ResizeObserver loop limit exceeded',
        'Non-Error promise rejection captured',
      ],
    })
    ```
  </Step>

  <Step title="Analytics & Metrics">
    ```typescript theme={null}
    // lib/analytics.ts
    import mixpanel from 'mixpanel-browser'
    import posthog from 'posthog-js'
    import * as amplitude from '@amplitude/analytics-browser'

    export class Analytics {
      constructor() {
        if (typeof window === 'undefined') return

        // Initialize based on discovery KPIs
        if (process.env.NEXT_PUBLIC_MIXPANEL_TOKEN) {
          mixpanel.init(process.env.NEXT_PUBLIC_MIXPANEL_TOKEN, {
            debug: process.env.NODE_ENV === 'development',
            track_pageview: true,
            persistence: 'localStorage',
          })
        }

        if (process.env.NEXT_PUBLIC_POSTHOG_KEY) {
          posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
            api_host: 'https://app.posthog.com',
            loaded: (posthog) => {
              if (process.env.NODE_ENV === 'development') {
                posthog.opt_out_capturing()
              }
            },
          })
        }

        if (process.env.NEXT_PUBLIC_AMPLITUDE_KEY) {
          amplitude.init(process.env.NEXT_PUBLIC_AMPLITUDE_KEY)
        }
      }

      identify(userId: string, traits?: Record<string, any>) {
        mixpanel?.identify(userId)
        mixpanel?.people.set(traits)
        posthog?.identify(userId, traits)
        amplitude?.setUserId(userId)
      }

      track(event: string, properties?: Record<string, any>) {
        // Track across all providers
        mixpanel?.track(event, properties)
        posthog?.capture(event, properties)
        amplitude?.track(event, properties)

        // Google Analytics 4
        if (window.gtag) {
          window.gtag('event', event, properties)
        }
      }

      // Track KPIs from discovery
      trackSignup(method: string) {
        this.track('user_signup', {
          method,
          timestamp: new Date().toISOString(),
        })
      }

      trackPurchase(amount: number, plan: string, currency = 'USD') {
        this.track('purchase_completed', {
          amount,
          plan,
          currency,
          timestamp: new Date().toISOString(),
        })
      }

      trackFeatureUsage(feature: string, metadata?: any) {
        this.track('feature_used', {
          feature,
          ...metadata,
        })
      }

      trackError(error: Error, context?: any) {
        this.track('error_occurred', {
          error_message: error.message,
          error_stack: error.stack,
          ...context,
        })
      }
    }

    export const analytics = new Analytics()
    ```
  </Step>

  <Step title="Performance Monitoring">
    ```typescript theme={null}
    // lib/performance.ts
    export class PerformanceMonitor {
      private metrics: Map<string, number[]> = new Map()

      measurePageLoad() {
        if (typeof window === 'undefined') return

        // Core Web Vitals
        const observer = new PerformanceObserver((list) => {
          for (const entry of list.getEntries()) {
            // Log to analytics
            analytics.track('web_vital', {
              name: entry.name,
              value: entry.startTime,
              rating: this.getRating(entry.name, entry.startTime),
            })

            // Send to monitoring service
            if (window.gtag) {
              window.gtag('event', entry.name, {
                value: Math.round(entry.startTime),
                metric_rating: this.getRating(entry.name, entry.startTime),
              })
            }
          }
        })

        observer.observe({ entryTypes: ['largest-contentful-paint'] })

        // First Input Delay
        new PerformanceObserver((list) => {
          const firstInput = list.getEntries()[0]
          const fid = firstInput.processingStart - firstInput.startTime

          analytics.track('web_vital', {
            name: 'FID',
            value: fid,
            rating: this.getRating('FID', fid),
          })
        }).observe({ type: 'first-input', buffered: true })

        // Cumulative Layout Shift
        let cls = 0
        new PerformanceObserver((list) => {
          for (const entry of list.getEntries()) {
            if (!entry.hadRecentInput) {
              cls += entry.value
            }
          }

          analytics.track('web_vital', {
            name: 'CLS',
            value: cls,
            rating: this.getRating('CLS', cls),
          })
        }).observe({ type: 'layout-shift', buffered: true })
      }

      private getRating(metric: string, value: number): 'good' | 'needs-improvement' | 'poor' {
        const thresholds = {
          LCP: [2500, 4000],
          FID: [100, 300],
          CLS: [0.1, 0.25],
          TTFB: [800, 1800],
        }

        const [good, poor] = thresholds[metric] || [0, 0]

        if (value <= good) return 'good'
        if (value <= poor) return 'needs-improvement'
        return 'poor'
      }

      // API Performance
      async measureAPICall<T>(
        name: string,
        fn: () => Promise<T>
      ): Promise<T> {
        const start = performance.now()

        try {
          const result = await fn()
          const duration = performance.now() - start

          this.recordMetric(name, duration)

          analytics.track('api_call', {
            endpoint: name,
            duration,
            success: true,
          })

          return result
        } catch (error) {
          const duration = performance.now() - start

          analytics.track('api_call', {
            endpoint: name,
            duration,
            success: false,
            error: error.message,
          })

          throw error
        }
      }

      private recordMetric(name: string, value: number) {
        if (!this.metrics.has(name)) {
          this.metrics.set(name, [])
        }

        const values = this.metrics.get(name)!
        values.push(value)

        // Keep only last 100 measurements
        if (values.length > 100) {
          values.shift()
        }
      }

      getMetricStats(name: string) {
        const values = this.metrics.get(name) || []

        if (values.length === 0) return null

        const sorted = [...values].sort((a, b) => a - b)

        return {
          min: Math.min(...values),
          max: Math.max(...values),
          avg: values.reduce((a, b) => a + b, 0) / values.length,
          p50: sorted[Math.floor(sorted.length * 0.5)],
          p95: sorted[Math.floor(sorted.length * 0.95)],
          p99: sorted[Math.floor(sorted.length * 0.99)],
        }
      }
    }

    export const performance = new PerformanceMonitor()
    ```
  </Step>

  <Step title="Health Checks">
    ```typescript theme={null}
    // app/api/health/route.ts
    import { NextResponse } from 'next/server'
    import { prisma } from '@/lib/prisma'
    import { redis } from '@/lib/redis'
    import { supabase } from '@/lib/supabase'

    export async function GET() {
      const checks = {
        status: 'healthy',
        timestamp: new Date().toISOString(),
        uptime: process.uptime(),
        environment: process.env.NODE_ENV,
        version: process.env.npm_package_version,
        checks: {
          app: 'unknown',
          database: 'unknown',
          cache: 'unknown',
          storage: 'unknown',
          external: 'unknown',
        },
        metrics: {
          memory: process.memoryUsage(),
          cpu: process.cpuUsage(),
        }
      }

      // Check application
      checks.checks.app = 'healthy'

      // Check database
      try {
        await prisma.$queryRaw`SELECT 1`
        checks.checks.database = 'healthy'
      } catch (error) {
        checks.checks.database = 'unhealthy'
        checks.status = 'degraded'
      }

      // Check cache
      try {
        await redis.ping()
        checks.checks.cache = 'healthy'
      } catch (error) {
        checks.checks.cache = 'unhealthy'
        checks.status = 'degraded'
      }

      // Check storage
      try {
        const { error } = await supabase.storage
          .from('health-check')
          .list('', { limit: 1 })

        checks.checks.storage = error ? 'unhealthy' : 'healthy'
      } catch (error) {
        checks.checks.storage = 'unhealthy'
        checks.status = 'degraded'
      }

      // Check external APIs
      try {
        const response = await fetch('https://api.example.com/health', {
          signal: AbortSignal.timeout(5000)
        })
        checks.checks.external = response.ok ? 'healthy' : 'unhealthy'
      } catch (error) {
        checks.checks.external = 'unhealthy'
        // Don't degrade status for external services
      }

      const statusCode = checks.status === 'healthy' ? 200 : 503

      return NextResponse.json(checks, { status: statusCode })
    }

    // Readiness check
    export async function HEAD() {
      try {
        await prisma.$queryRaw`SELECT 1`
        return new Response(null, { status: 200 })
      } catch {
        return new Response(null, { status: 503 })
      }
    }
    ```
  </Step>
</Steps>

## Infrastructure as Code

<CodeGroup>
  ```terraform Terraform Configuration theme={null}
  # terraform/main.tf
  terraform {
    required_providers {
      vercel = {
        source  = "vercel/vercel"
        version = "~> 0.4"
      }
      cloudflare = {
        source  = "cloudflare/cloudflare"
        version = "~> 4.0"
      }
    }

    backend "s3" {
      bucket = "terraform-state"
      key    = "production/terraform.tfstate"
      region = "us-east-1"
    }
  }

  # Vercel Project
  resource "vercel_project" "main" {
    name      = var.project_name
    framework = "nextjs"

    git_repository = {
      type = "github"
      repo = var.github_repo
    }

    environment_variables = {
      NEXT_PUBLIC_APP_URL = {
        value = var.app_url
        target = ["production"]
      }
    }

    build_command    = "pnpm build"
    output_directory = ".next"
    install_command  = "pnpm install"
  }

  # Cloudflare DNS
  resource "cloudflare_record" "main" {
    zone_id = var.cloudflare_zone_id
    name    = "@"
    value   = vercel_project.main.alias[0].domain
    type    = "CNAME"
    proxied = true
  }

  resource "cloudflare_record" "www" {
    zone_id = var.cloudflare_zone_id
    name    = "www"
    value   = vercel_project.main.alias[0].domain
    type    = "CNAME"
    proxied = true
  }

  # Cloudflare Page Rules
  resource "cloudflare_page_rule" "cache" {
    zone_id = var.cloudflare_zone_id
    target  = "${var.domain}/_next/static/*"

    actions {
      cache_level = "cache_everything"
      edge_cache_ttl = 86400
    }
  }
  ```

  ```yaml Ansible Playbook theme={null}
  # ansible/deploy.yml
  ---
  - name: Deploy Application
    hosts: production
    vars:
      app_name: nextjs-app
      deploy_path: /var/www/app
      node_version: 20

    tasks:
      - name: Install Node.js
        nvm:
          version: "{{ node_version }}"
          state: present

      - name: Install pnpm
        npm:
          name: pnpm
          global: true

      - name: Clone repository
        git:
          repo: "{{ git_repo }}"
          dest: "{{ deploy_path }}"
          version: "{{ git_branch | default('main') }}"

      - name: Install dependencies
        command: pnpm install --frozen-lockfile
        args:
          chdir: "{{ deploy_path }}"

      - name: Build application
        command: pnpm build
        args:
          chdir: "{{ deploy_path }}"
        environment:
          NODE_ENV: production

      - name: Setup PM2
        npm:
          name: pm2
          global: true

      - name: Start application
        command: pm2 start ecosystem.config.js
        args:
          chdir: "{{ deploy_path }}"

      - name: Save PM2 process list
        command: pm2 save

      - name: Configure Nginx
        template:
          src: nginx.conf.j2
          dest: /etc/nginx/sites-available/{{ app_name }}
        notify: restart nginx

      - name: Enable site
        file:
          src: /etc/nginx/sites-available/{{ app_name }}
          dest: /etc/nginx/sites-enabled/{{ app_name }}
          state: link
        notify: restart nginx

    handlers:
      - name: restart nginx
        service:
          name: nginx
          state: restarted
  ```
</CodeGroup>

***

<Card title="Next Step" icon="arrow-right" href="/ai-development/prompting/agents">
  With deployment configured, proceed to agent collaboration strategies →
</Card>
