---
title: 'Deploy TanStack Start on AWS'
description: 'Deploy TanStack Start applications on AWS using static site generation with S3 and CloudFront, serverless SSR with Lambda, or containerized SSR with ECS Fargate.'
---
import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';
import PatternList from '../../../components/docs/PatternList.astro';
import FrameworkHero from '../../../components/docs/FrameworkHero.astro';
Deploy your [TanStack Start](https://tanstack.com/start) applications to AWS using Thunder. Choose the pattern that fits your app's needs.
## Available Patterns
## Prerequisites
## Getting Started
### Create Project
Scaffold a new TanStack Start project using your preferred package manager. This sets up the project structure, installs dependencies, and prepares you for development.
```sh
bunx create-tanstack-start my-app
cd my-app
```
```sh
npx create-tanstack-start my-app
cd my-app
```
```sh
pnpm create @tanstack/start@latest my-app
cd my-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
```
---
## TanStack Start Static Site Deployment
Deploy a fully pre-rendered TanStack Start site to [S3](https://aws.amazon.com/s3/) with [CloudFront](https://aws.amazon.com/cloudfront/) as the CDN. Every page is generated at build time and served as static files — no server required.
### Configure
Set the Nitro preset to `static` in your app config. This tells TanStack Start to pre-render all routes at build time and output them to `.output/public/`.
```ts title="app.config.ts"
import { defineConfig } from '@tanstack/start/config';
export default defineConfig({
server: {
preset: 'static',
},
});
```
### 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: '.output/public',
};
new Static(new Cdk.App(), `${config.application}-${config.service}-${config.environment}-stack`, config);
```
### Deploy
Build your app first to generate the static files, 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.
---
## TanStack Start Containerized Deployment with Fargate
Run your TanStack Start 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, and any server-side logic.
### Configure for Node Server
TanStack Start uses [Nitro](https://nitro.unjs.io/) as its server engine. Set the preset to `node-server` so the build output is a standard Node.js HTTP server that can run inside a container.
```ts title="app.config.ts"
import { defineConfig } from '@tanstack/start/config';
export default defineConfig({
server: {
preset: 'node-server',
},
});
```
### 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 keeps the final image lean by separating the build environment from the runtime.
```dockerfile title="Dockerfile"
FROM oven/bun:latest AS builder
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
FROM oven/bun:latest AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV HOST=0.0.0.0
ENV PORT=3000
COPY --from=builder /app/.output ./
EXPOSE 3000
CMD ["bun", "run", "server/index.mjs"]
```
### 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' },
],
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.
---
## TanStack Start Serverless Fullstack Deployment
Deploy TanStack Start with SSR using [AWS Lambda](https://aws.amazon.com/lambda/) for server-side rendering, [S3](https://aws.amazon.com/s3/) for static assets, and [CloudFront](https://aws.amazon.com/cloudfront/) to unify both behind a single domain. This pattern scales to zero and charges only for actual requests.
### Configure TanStack Start for AWS Lambda
TanStack Start uses [Nitro](https://nitro.unjs.io/) as its server engine. You must explicitly set the `aws-lambda` preset — the default `node-server` preset outputs an HTTP server, not a Lambda handler, and will not work on Lambda.
```ts title="app.config.ts"
import { defineConfig } from '@tanstack/start/config';
import { nitro } from 'nitro/vite';
export default defineConfig({
vite: {
plugins: [
nitro({ preset: 'aws-lambda' }),
],
},
});
```
The build will produce `.output/server/` (Lambda handler) and `.output/public/` (static assets for S3).
### Stack (Zip mode)
The `TanStackStart` construct wires up Lambda, API Gateway, S3, and CloudFront automatically. By default, Thunder packages your Lambda handler as a Zip deployment — the fastest option for most apps.
```ts title="stack/prod.ts"
import { Cdk, TanStackStart, type TanStackStartProps } from '@thunder-so/thunder';
const config: TanStackStartProps = {
env: { account: 'YOUR_ACCOUNT_ID', region: 'us-east-1' },
application: 'myapp',
service: 'web',
environment: 'prod',
rootDir: '.',
};
new TanStackStart(new Cdk.App(), `${config.application}-${config.service}-${config.environment}-stack`, config);
```
### Container Mode
Zip deployments have a 250 MB unzipped size limit. If your app has large dependencies — native modules, ML libraries, or heavy assets — switch to container mode. Thunder builds a Docker image, pushes it to [ECR](https://aws.amazon.com/ecr/), and deploys it as a [container Lambda](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html), which supports up to 10 GB.
#### Stack (Container mode)
Add `dockerFile` to `serverProps` to enable container mode.
```ts title="stack/prod.ts"
const config: TanStackStartProps = {
// ...
serverProps: {
dockerFile: 'Dockerfile',
memorySize: 2048,
},
};
```
#### Dockerfile
```dockerfile title="Dockerfile"
FROM public.ecr.aws/lambda/nodejs:22
# Copy all lambda files
COPY . ./
CMD ["index.handler"]
```
### Environment Variables and Secrets
Runtime environment variables are injected into the Lambda function 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: TanStackStartProps = {
// ...
serverProps: {
variables: [
{ NODE_ENV: 'production' },
{ 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
Build your app first to generate the Lambda handler and static assets, then deploy with CDK.
```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** that serves both your SSR responses and static assets.