--- title: Fargate description: Deploy containerized applications using ECS Fargate and ALB --- import { Icon } from 'astro-icon/components'; import { Aside } from '@astrojs/starlight/components'; import FargateArchitecture from '@/components/docs/patterns/FargateArchitecture.astro'; import FargatePipeline from '@/components/docs/patterns/FargatePipeline.astro'; Deploy containerized web services on [AWS ECS Fargate](https://aws.amazon.com/fargate/) with an [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/) for scalable, managed container hosting. No EC2 instances, no cluster management — automatic health checks and rolling updates. ## Supported Frameworks - [Next.js](https://nextjs.org/) - [Nuxt](https://nuxt.com/) - [TanStack Start](https://tanstack.com/start/latest) - [SvelteKit](https://kit.svelte.dev/) - [Astro](https://astro.build/) - [NestJS](https://nestjs.com/) - [Hono](https://hono.dev/) - Any containerized web application ## AWS Resources | Resource | Purpose | | --- | --- | | [ECS Cluster](https://aws.amazon.com/ecs/) | Container orchestration | | [Fargate Task](https://aws.amazon.com/fargate/) | Serverless container runtime | | [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/) | Public HTTP/HTTPS endpoint, health checks | | [VPC](https://aws.amazon.com/vpc/) | Network isolation (created automatically if not provided) | | [ECR Repository](https://aws.amazon.com/ecr/) | Container image registry (via CodePipeline) | | [CloudWatch Logs](https://aws.amazon.com/cloudwatch/) | Container logs, retained for 1 week | | [ACM Certificate](https://aws.amazon.com/certificate-manager/) | SSL for custom domain (optional) | | [Route53](https://aws.amazon.com/route53/) | DNS A record (optional) | ## Container Service Architecture ## Quick Start ### Installation ```bash bun add -D @thunder-so/thunder ``` ### Configuration ```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, desiredCount: 1, cpu: 512, memorySize: 1024, port: 3000, healthCheckPath: '/', }, }; new Fargate( new Cdk.App(), `${config.application}-${config.service}-${config.environment}-stack`, config ); ``` ### Deploy ```bash npx cdk deploy --app "bunx tsx stack/prod.ts" --profile default ``` CDK builds your Docker image, pushes it to ECR, and deploys the service: ``` Outputs: myapp-web-prod-stack.LoadBalancerDNS = myapp-web-prod-1234567890.us-east-1.elb.amazonaws.com ``` ## Custom Domain Connect your service to a custom domain. The certificate must be issued in the **same region as your Fargate service**. ```ts title="stack/prod.ts" const config: FargateProps = { // ... domain: 'app.example.com', hostedZoneId: 'Z1D633PJN98FT9', regionalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123', }; ``` When a domain is configured: HTTPS listener is added on port 443, HTTP on port 80 redirects to HTTPS, and a Route53 A record is created. ## Service Configuration ### Environment Variables and Secrets ```ts title="stack/prod.ts" const config: FargateProps = { // ... serviceProps: { // ... 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', }, ], }, }; ``` Secrets are injected as environment variables at container startup. The task role is automatically granted read access. ### CPU and Memory Fargate uses fixed CPU/memory combinations. ARM64 (`Cdk.aws_ecs.CpuArchitecture.ARM64`) is cheaper and often faster for Node.js workloads. | CPU (units) | vCPU | Valid Memory (MB) | | --- | --- | --- | | 256 | 0.25 | 512, 1024, 2048 | | 512 | 0.5 | 1024–4096 | | 1024 | 1 | 2048–8192 | | 2048 | 2 | 4096–16384 | | 4096 | 4 | 8192–30720 | ## Nixpacks Integration Use [Nixpacks](https://nixpacks.com/) to automatically generate a container image without writing a Dockerfile. When no `dockerFile` is set in `serviceProps`, Thunder runs Nixpacks during `cdk synth` to detect your runtime and generate an optimized build. Nixpacks auto-detects Node.js, Python, Go, Ruby, Rust, PHP, Java, Deno, and more. ```ts title="stack/prod.ts" const config: FargateProps = { // ... serviceProps: { // no dockerFile — Nixpacks takes over port: 3000, cpu: 512, memorySize: 1024, }, buildProps: { runtime_version: '22', // Node.js version installcmd: 'bun install', buildcmd: 'bun run build', startcmd: 'bun start', }, }; ``` For advanced control, add a `nixpacks.toml` to your project root: ```toml title="nixpacks.toml" [phases.install] cmds = ["bun install --frozen-lockfile"] [phases.build] cmds = ["bun run build"] [start] cmd = "node dist/server.js" ``` ## Custom Dockerfile For full control over the build environment, provide your own Dockerfile. The entire `rootDir` is used as the Docker build context. ```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 PORT=3000 COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./ EXPOSE 3000 CMD ["node", "dist/server.js"] ``` ## Estimated Cost | Scenario | Monthly (us-east-1, no free tier) | | --- | --- | | 1 task (0.25 vCPU / 512 MB) | ~$33 | | 2 tasks (0.5 vCPU / 1 GB each) | ~$47 | The ALB (~$22/month) is the dominant fixed cost. See [Fargate pricing](https://aws.amazon.com/fargate/pricing/) and [ALB pricing](https://aws.amazon.com/elasticloadbalancing/pricing/). ## CI/CD Pipeline ### Nixpacks Pipeline ```ts title="stack/prod.ts" const config: FargateProps = { // ... accessTokenSecretArn: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:github-token-XXXXXX', sourceProps: { owner: 'your-username', repo: 'your-repo', branchOrRef: 'main', }, buildProps: { installcmd: 'bun install', buildcmd: 'bun run build', startcmd: 'bun start', }, }; ``` ### Custom Dockerfile Pipeline ```ts title="stack/prod.ts" const config: FargateProps = { // ... accessTokenSecretArn: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:github-token-XXXXXX', sourceProps: { owner: 'your-username', repo: 'your-repo', branchOrRef: 'main', }, serviceProps: { dockerFile: 'Dockerfile', port: 3000, }, }; ``` ## Stack Outputs | Output | Description | | --- | --- | | `LoadBalancerDNS` | ALB DNS name | | `Route53Domain` | Custom domain URL (only if `domain` is configured) | ## Destroy ```bash npx cdk destroy --app "bunx tsx stack/prod.ts" --profile default ```