StarterApp Docs
Deployment

Deploy to AWS

Enterprise-scale deployment with full infrastructure control

AWS provides complete infrastructure control for applications requiring specific compliance, scaling, or regional requirements. The framework supports AWS Amplify (recommended), ECS with Fargate, and serverless Lambda.

Pro Tip

AWS Amplify offers the fastest path to production with automatic scaling and preview deployments. For containerized control, use ECS with Fargate. For maximum cost efficiency, consider serverless Lambda.

Create App

Navigate to AWS Amplify Console and select New appHost web app.

Connect your Git repository (GitHub, GitLab, or Bitbucket). Amplify detects Next.js automatically.

Monorepo Detection

Deploy apps/marketing and apps/dashboard as separate Amplify apps. Each requires its own project with distinct build configurations.

Configure Build Settings

Select Monorepo and configure:

version: 1
applications:
  - frontend:
      phases:
        preBuild:
          commands:
            - cd ../..
            - npm install -g pnpm@9
            - pnpm install --frozen-lockfile
        build:
          commands:
            - pnpm turbo run build --filter=marketing
      artifacts:
        baseDirectory: apps/marketing/.next
        files:
          - '**/*'
      cache:
        paths:
          - node_modules/**/*
          - .turbo/**/*
      appRoot: apps/marketing

Turbo Cache Acceleration

The cache paths preserve Turbo's build cache across deployments, reducing build times from minutes to seconds after the first deploy.

version: 1
applications:
  - frontend:
      phases:
        preBuild:
          commands:
            - cd ../..
            - npm install -g pnpm@9
            - pnpm install --frozen-lockfile
        build:
          commands:
            - pnpm turbo run build --filter=dashboard
      artifacts:
        baseDirectory: apps/dashboard/.next
        files:
          - '**/*'
      cache:
        paths:
          - node_modules/**/*
          - .turbo/**/*
      appRoot: apps/dashboard

Set Environment Variables

In Environment variables, configure production credentials:

# Core Application (update after deploy)
APP_BASE_URL=https://your-amplify-domain.amplifyapp.com
DASHBOARD_BASE_URL=https://dashboard.amplifyapp.com
NEXT_PUBLIC_DASHBOARD_BASE_URL=https://dashboard.amplifyapp.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
ENABLE_HSTS=1
HSTS_PRELOAD=0
TRUSTED_IP_HEADER=x-forwarded-for

# AWS Amplify
_LIVE_UPDATES=[{"pkg":"@aws-amplify/cli","type":"npm","version":"latest"}]

# Analytics (optional)
NEXT_PUBLIC_POSTHOG_KEY=phc_<your-key>
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
# Preview environment uses staging credentials
APP_BASE_URL=https://preview.amplifyapp.com
NEXT_PUBLIC_CONVEX_URL=https://your-preview.convex.cloud
CONVEX_DEPLOYMENT=preview

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

# Relaxed security for previews
ENABLE_HSTS=0
HSTS_PRELOAD=0

Sync Convex Environment

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

# Set production deployment
npx convex deploy --prod

# Configure environment variables
npx convex env set APP_BASE_URL https://main.app-id.amplifyapp.com --prod
npx convex env set DASHBOARD_BASE_URL https://dashboard.app-id.amplifyapp.com --prod
npx convex env set ALLOWED_WEB_ORIGINS https://main.app-id.amplifyapp.com --prod
npx convex env set BETTER_AUTH_SECRET <same-value-as-amplify> --prod
npx convex env set GOOGLE_CLIENT_ID <same-value-as-amplify> --prod
npx convex env set GOOGLE_CLIENT_SECRET <same-value-as-amplify> --prod
npx convex env set AUTUMN_SECRET_KEY <same-value-as-amplify> --prod

Environment Parity Critical

Mismatched environment variables between Amplify and Convex cause authentication failures and billing errors. Rerun these commands whenever credentials change.

Deploy and Update URLs

Click Save and deploy. Amplify compiles and distributes to AWS infrastructure.

After deployment completes, Amplify assigns:

https://main.[app-id].amplifyapp.com

Update Environment variables with this production URL:

APP_BASE_URL=https://main.[app-id].amplifyapp.com
ALLOWED_WEB_ORIGINS=https://main.[app-id].amplifyapp.com

Trigger redeploy via App settings → Redeploy this version.

Configure OAuth Callbacks

Update OAuth providers with production domain:

Option 2: ECS with Fargate

Container deployment provides complete control over runtime environment. ECS with Fargate eliminates server management while maintaining flexibility. Teams scale precisely based on traffic patterns, optimizing costs while ensuring performance.

Create Dockerfile

Add to repository root:

FROM node:20-alpine AS builder
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Install pnpm
RUN corepack enable && corepack prepare pnpm@9 --activate

# Copy monorepo configs
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./
COPY apps/marketing/package.json ./apps/marketing/
COPY packages/ ./packages/

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source and build
COPY apps/marketing/ ./apps/marketing/
RUN pnpm turbo run build --filter=marketing

# Production image
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

# Copy built artifacts
COPY --from=builder /app/apps/marketing/.next/standalone ./
COPY --from=builder /app/apps/marketing/.next/static ./apps/marketing/.next/static
COPY --from=builder /app/apps/marketing/public ./apps/marketing/public

EXPOSE 3000
CMD ["node", "apps/marketing/server.js"]

Standalone Output Required

Ensure next.config.ts includes output: 'standalone' for optimized container images.

Push to ECR

# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin $ECR_URI

# Build and tag
docker build -t starterapp-marketing .
docker tag starterapp-marketing:latest $ECR_URI/starterapp-marketing:latest

# Push to registry
docker push $ECR_URI/starterapp-marketing:latest

Create ECS Task Definition

{
  "family": "starterapp-marketing",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "containerDefinitions": [
    {
      "name": "marketing",
      "image": "$ECR_URI/starterapp-marketing:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "APP_BASE_URL",
          "value": "https://your-domain.com"
        }
      ],
      "secrets": [
        {
          "name": "BETTER_AUTH_SECRET",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:prod/auth-secret"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/starterapp-marketing",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

Deploy Service

# Create ECS service
aws ecs create-service \
  --cluster starterapp-cluster \
  --service-name marketing \
  --task-definition starterapp-marketing \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=marketing,containerPort=3000"

ECS handles rolling deployments with zero downtime.

Option 3: Serverless Lambda

Serverless deployment eliminates all infrastructure management. Applications scale automatically from zero to millions of requests, charging only for actual usage. This option maximizes cost efficiency for variable traffic patterns.

Install SST

pnpm add -D sst

Why SST over Serverless Framework

SST provides native Next.js 15 support with streaming, edge runtime compatibility, and superior local development experience.

Configure Infrastructure

Create sst.config.ts at repository root:

import { SSTConfig } from "sst";
import { NextjsSite } from "sst/constructs";

export default {
  config(_input) {
    return {
      name: "starterapp",
      region: "us-east-1",
    };
  },
  stacks(app) {
    app.stack(function Site({ stack }) {
      const marketing = new NextjsSite(stack, "marketing", {
        path: "apps/marketing",
        buildCommand: "cd ../.. && pnpm turbo run build --filter=marketing",
        environment: {
          APP_BASE_URL: process.env.APP_BASE_URL!,
          NEXT_PUBLIC_CONVEX_URL: process.env.NEXT_PUBLIC_CONVEX_URL!,
        },
      });

      stack.addOutputs({
        MarketingUrl: marketing.url,
      });
    });
  },
} satisfies SSTConfig;

Deploy

# Deploy to production
npx sst deploy --stage prod

SST creates Lambda functions, API Gateway, and CloudFront distribution automatically.

AWS Service Integration

Troubleshooting