--- title: 'Deploy Next.js on AWS' description: 'Deploy Next.js applications on AWS using static export with S3 and CloudFront, or full-stack SSR with ECS Fargate and Application Load Balancer.' --- import { Tabs, TabItem, Aside } from '@astrojs/starlight/components'; import PatternList from '../../../components/docs/PatternList.astro'; import FrameworkHero from '../../../components/docs/FrameworkHero.astro'; Deploy your [Next.js](https://nextjs.org/) applications to AWS using Thunder. Choose the pattern that fits your app's needs. ## Available Patterns ## Prerequisites ## Getting Started ### Create Project Scaffold a new Next.js project using your preferred package manager. This sets up the project structure, installs dependencies, and prepares you for development. ```sh bunx create-next-app@latest my-nextjs-app cd my-nextjs-app ``` ```sh npm create next-app@latest my-nextjs-app cd my-nextjs-app ``` ```sh pnpm create next-app my-nextjs-app cd my-nextjs-app ``` ### Install Thunder Add Thunder as a development dependency. It provides the CDK constructs you'll use to define your AWS infrastructure. ```sh bun add @thunder-so/thunder --development ``` ```sh npm install @thunder-so/thunder --save-dev ``` ```sh pnpm add -D @thunder-so/thunder ``` --- ## Next.js Static Export Deployment Deploy Next.js as a fully static site to [S3](https://aws.amazon.com/s3/) with [CloudFront](https://aws.amazon.com/cloudfront/) as the CDN. In [static export mode](https://nextjs.org/docs/pages/guides/static-exports), Next.js pre-renders all pages at build time and outputs plain HTML, CSS, and JavaScript — no server required. ### Configure Set `output: 'export'` in your Next.js config to enable static export mode. Setting `distDir` to `dist` keeps the output directory consistent with other frameworks. ```ts title="next.config.ts" import type { NextConfig } from 'next'; const nextConfig: NextConfig = { output: 'export', distDir: 'dist', }; export default nextConfig; ``` ### Stack The `Static` construct provisions an S3 bucket, a CloudFront distribution, and optionally a Route53 DNS record. ```ts title="stack/prod.ts" import { Cdk, Static, type StaticProps } from '@thunder-so/thunder'; const config: StaticProps = { env: { account: 'YOUR_ACCOUNT_ID', region: 'us-east-1' }, application: 'myapp', service: 'web', environment: 'prod', rootDir: '.', outputDir: 'dist', }; new Static(new Cdk.App(), `${config.application}-${config.service}-${config.environment}-stack`, config); ``` ### Deploy Build your Next.js app first to generate the static export, then deploy with CDK. CDK uploads the files to S3 and provisions the CloudFront distribution. ```sh bun run build npx cdk deploy --app "bunx tsx stack/prod.ts" --profile default ``` ```sh npm run build npx cdk deploy --app "npx tsx stack/prod.ts" --profile default ``` ```sh pnpm run build pnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts" --profile default ``` After deployment, CDK outputs a **CloudFront URL** where your static site is live. --- ## Next.js Containerized Deployment with Fargate Run your Next.js app as a Node.js server inside a Docker container on [ECS Fargate](https://aws.amazon.com/fargate/). Traffic is routed through an [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/). This pattern supports full SSR, API routes, image optimization, and all Next.js features. ### Configure for Node Server Set `output: 'standalone'` in your Next.js config. This tells Next.js to produce a minimal, self-contained server bundle in `.next/standalone/` that includes only the files needed to run the app — ideal for Docker. ```ts title="next.config.ts" import type { NextConfig } from 'next'; const nextConfig: NextConfig = { output: 'standalone', }; export default nextConfig; ``` ### Stack The `Fargate` construct creates an ECS cluster, a Fargate task definition, and an Application Load Balancer. ```ts title="stack/prod.ts" import { Cdk, Fargate, type FargateProps } from '@thunder-so/thunder'; const config: FargateProps = { env: { account: 'YOUR_ACCOUNT_ID', region: 'us-east-1' }, application: 'myapp', service: 'web', environment: 'prod', rootDir: '.', serviceProps: { dockerFile: 'Dockerfile', architecture: Cdk.aws_ecs.CpuArchitecture.ARM64, cpu: 512, memorySize: 1024, port: 3000, desiredCount: 1, healthCheckPath: '/', }, }; new Fargate(new Cdk.App(), `${config.application}-${config.service}-${config.environment}-stack`, config); ``` ### Dockerfile Create a `Dockerfile` in your project root. The multi-stage build uses Bun to install dependencies and build the app, then copies only the standalone output into a minimal Node.js runtime image. ```dockerfile title="Dockerfile" FROM public.ecr.aws/docker/library/node:22-alpine AS builder WORKDIR /app COPY package.json bun.lockb ./ RUN curl -fsSL https://bun.sh/install | bash && export PATH="$HOME/.bun/bin:$PATH" RUN bun install --frozen-lockfile COPY . . RUN bun run build FROM public.ecr.aws/docker/library/node:22-alpine AS runner WORKDIR /app ENV NODE_ENV=production ENV HOSTNAME=0.0.0.0 ENV PORT=3000 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /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 CMD ["node", "server.js"] ``` ### Environment Variables and Secrets Runtime environment variables are injected into the Fargate task at deploy time. For sensitive values, store them in [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) and reference them by ARN — Thunder fetches and injects them automatically. ```ts title="stack/prod.ts" const config: FargateProps = { // ... serviceProps: { // ... variables: [ { NODE_ENV: 'production' }, { NEXT_PUBLIC_API_URL: 'https://api.example.com' }, ], secrets: [ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:/myapp/DATABASE_URL-abc123', }, ], }, }; ``` ### Deploy CDK builds the Docker image, pushes it to [ECR](https://aws.amazon.com/ecr/), and deploys it to Fargate. No manual Docker commands needed. ```sh npx cdk deploy --app "bunx tsx stack/prod.ts" --profile default ``` ```sh npx cdk deploy --app "npx tsx stack/prod.ts" --profile default ``` ```sh pnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts" --profile default ``` After deployment, CDK outputs the **Load Balancer DNS** for your application.