Deploy your Next.js 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.
bunx create-next-app@latest my-nextjs-appcd my-nextjs-appnpm create next-app@latest my-nextjs-appcd my-nextjs-apppnpm create next-app my-nextjs-appcd my-nextjs-appInstall Thunder
Add Thunder as a development dependency. It provides the CDK constructs you’ll use to define your AWS infrastructure.
bun add @thunder-so/thunder --developmentnpm install @thunder-so/thunder --save-devpnpm add -D @thunder-so/thunderNext.js Static Export Deployment
Deploy Next.js as a fully static site to S3 with CloudFront as the CDN. In static export mode, 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.
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.
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.
bun run buildnpx cdk deploy --app "bunx tsx stack/prod.ts" --profile defaultnpm run buildnpx cdk deploy --app "npx tsx stack/prod.ts" --profile defaultpnpm run buildpnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts" --profile defaultAfter 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. Traffic is routed through an 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.
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.
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.
FROM public.ecr.aws/docker/library/node:22-alpine AS builderWORKDIR /appCOPY package.json bun.lockb ./RUN curl -fsSL https://bun.sh/install | bash && export PATH="$HOME/.bun/bin:$PATH"RUN bun install --frozen-lockfileCOPY . .RUN bun run build
FROM public.ecr.aws/docker/library/node:22-alpine AS runnerWORKDIR /appENV NODE_ENV=productionENV HOSTNAME=0.0.0.0ENV PORT=3000RUN addgroup --system --gid 1001 nodejsRUN adduser --system --uid 1001 nextjsCOPY --from=builder /app/public ./publicCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/staticUSER nextjsEXPOSE 3000CMD ["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 and reference them by ARN — Thunder fetches and injects them automatically.
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, and deploys it to Fargate. No manual Docker commands needed.
npx cdk deploy --app "bunx tsx stack/prod.ts" --profile defaultnpx cdk deploy --app "npx tsx stack/prod.ts" --profile defaultpnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts" --profile defaultAfter deployment, CDK outputs the Load Balancer DNS for your application.