--- title: 'Deploy SvelteKit on AWS' description: 'Deploy SvelteKit 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 [SvelteKit](https://svelte.dev/docs/kit) applications to AWS using Thunder. Choose the pattern that fits your app's needs. ## Available Patterns ## Prerequisites ## Getting Started ### Create Project Scaffold a new SvelteKit project using your preferred package manager. This sets up the project structure, installs dependencies, and prepares you for development. ```sh bunx sv create my-sveltekit-app cd my-sveltekit-app ``` ```sh npx sv create my-sveltekit-app cd my-sveltekit-app ``` ```sh pnpm dlx sv create my-sveltekit-app cd my-sveltekit-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 ``` --- ## SvelteKit Static Site Deployment Deploy a fully pre-rendered SvelteKit site to [S3](https://aws.amazon.com/s3/) with [CloudFront](https://aws.amazon.com/cloudfront/) as the CDN. This pattern supports both SSG (pre-rendered pages) and SPA mode (client-side routing with a single HTML shell). No server required. ### Configure Install the static adapter and configure SvelteKit to use it. Then enable prerendering globally by setting `prerender = true` in your root layout. ```sh bun add -D @sveltejs/adapter-static ``` ```sh npm install -D @sveltejs/adapter-static ``` ```sh pnpm add -D @sveltejs/adapter-static ``` ```js title="svelte.config.js" import adapter from '@sveltejs/adapter-static'; export default { kit: { adapter: adapter(), }, }; ``` ```ts title="src/routes/+layout.ts" export const prerender = true; ``` ### Stack The `Static` construct provisions an S3 bucket, a CloudFront distribution, and optionally a Route53 DNS record. SvelteKit's static adapter outputs to `build/` by default. ```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: 'build', }; new Static(new Cdk.App(), `${config.application}-${config.service}-${config.environment}-stack`, config); ``` ### Deploy Build your SvelteKit site first, then deploy with CDK. CDK uploads the static 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. --- ## SvelteKit Containerized Deployment with Fargate Run your SvelteKit 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 Install the Node adapter and configure SvelteKit to use it. This produces a standard Node.js HTTP server in `build/` that can run inside a container. ```sh bun add -D @sveltejs/adapter-node ``` ```sh npm install -D @sveltejs/adapter-node ``` ```sh pnpm add -D @sveltejs/adapter-node ``` ```js title="svelte.config.js" import adapter from '@sveltejs/adapter-node'; export default { kit: { adapter: adapter(), }, }; ``` ### 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/build ./build COPY --from=builder /app/package.json ./ EXPOSE 3000 CMD ["bun", "./build/index.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' }, ], 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. --- ## SvelteKit Serverless Fullstack Deployment Deploy SvelteKit 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. ### Install Adapter for Lambda The `@foladayo/sveltekit-adapter-lambda` package adapts SvelteKit's build output to the [Lambda function handler](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html) format expected by API Gateway. ```sh bun add -D @foladayo/sveltekit-adapter-lambda ``` ```sh npm install -D @foladayo/sveltekit-adapter-lambda ``` ```sh pnpm add -D @foladayo/sveltekit-adapter-lambda ``` ### Configure SvelteKit for AWS Lambda Replace the adapter in your SvelteKit config. The `serveStatic: true` option is required — it tells the adapter to serve prerendered pages directly from the Lambda handler rather than expecting a separate static file server. ```js title="svelte.config.js" import adapter from '@foladayo/sveltekit-adapter-lambda'; export default { kit: { adapter: adapter({ serveStatic: true, }), }, }; ``` The build will produce a flat `build/` directory (Lambda handler) and `build/client/` (static assets for S3). ### Stack (Zip mode) The `SvelteKit` 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, SvelteKit, type SvelteKitProps } from '@thunder-so/thunder'; const config: SvelteKitProps = { env: { account: 'YOUR_ACCOUNT_ID', region: 'us-east-1' }, application: 'myapp', service: 'web', environment: 'prod', rootDir: '.', }; new SvelteKit(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: SvelteKitProps = { // ... 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: SvelteKitProps = { // ... serverProps: { variables: [ { NODE_ENV: 'production' }, ], secrets: [ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:/myapp/DATABASE_URL-abc123', }, ], }, }; ``` ### Deploy Build your SvelteKit 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.