Run any Docker container as a production web service on AWS ECS Fargate - serverless containers with an Application Load Balancer in front. No EC2 instances, no cluster management, and automatic health checks and restarts.
Works with any language or framework you can containerize: Next.js, Express, NestJS, Django, Rails, Go, Rust, and more.
| Resource | Purpose |
|---|---|
| ECS Cluster | Container orchestration |
| Fargate Task | Serverless container runtime |
| Application Load Balancer | Public HTTP/HTTPS endpoint, health checks |
| VPC | Network isolation (created automatically if not provided) |
| CloudWatch Logs | Container logs, retained for 1 week |
| ACM Certificate | SSL for custom domain (optional) |
| Route53 | DNS A record (optional) |
- Docker installed and running locally
- AWS CLI configured
- AWS CDK bootstrapped:
cdk bootstrap aws://YOUR_ACCOUNT_ID/us-east-1
bun add @thunder-so/thunder --development
# or
npm install @thunder-so/thunder --save-devYour app needs a Dockerfile. Here's a production-ready example for a Node.js app:
# Dockerfile
# Build stage
FROM public.ecr.aws/docker/library/node:22-alpine AS builder
WORKDIR /app
COPY . .
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN pnpm install
RUN pnpm run build
# Production stage
FROM public.ecr.aws/docker/library/node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY --from=builder /app/ ./
EXPOSE 3000
CMD ["pnpm", "start"]Add a .dockerignore to keep the build context lean:
.git
node_modules
cdk.out
stack
.DS_Store
import { Cdk, Fargate, type FargateProps } from "@thunder-so/thunder";
const config: FargateProps = {
env: {
account: "123456789012",
region: "us-east-1",
},
application: "myapp",
service: "api",
environment: "dev",
rootDir: ".",
serviceProps: {
dockerFile: "Dockerfile",
architecture: Cdk.aws_ecs.CpuArchitecture.ARM64,
cpu: 256, // 0.25 vCPU
memorySize: 512, // 512 MB
port: 3000,
desiredCount: 1,
healthCheckPath: "/health",
},
};
new Fargate(
new Cdk.App(),
`${config.application}-${config.service}-${config.environment}-stack`,
config,
);npx cdk deploy --app "npx tsx stack/dev.ts" --profile defaultCDK builds your Docker image, pushes it to ECR, and deploys the service. The ALB DNS name is output:
Outputs:
myapp-api-dev-stack.LoadBalancerDNS = myapp-api-dev-1234567890.us-east-1.elb.amazonaws.com
- Create a Route53 Hosted Zone
- Request an ACM certificate in the same region as your service
const config: FargateProps = {
// ...
domain: "api.example.com",
regionalCertificateArn:
"arn:aws:acm:us-east-1:123456789012:certificate/abc-123",
hostedZoneId: "Z1234567890ABC",
};When a domain is configured:
- HTTPS listener is added on port 443
- HTTP on port 80 redirects to HTTPS
- Route53 A record is created pointing to the ALB
serviceProps: {
// ...
variables: [
{ NODE_ENV: 'production' },
{ PORT: '3000' },
],
},aws secretsmanager create-secret \
--name "/myapp/DATABASE_URL" \
--secret-string "postgres://user:pass@host/db"serviceProps: {
// ...
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.
The ALB and ECS both perform health checks. By default they hit /. Configure a dedicated health endpoint:
serviceProps: {
healthCheckPath: '/health',
},Your app should return 200 OK on that path within 5 seconds.
Fargate uses fixed CPU/memory combinations:
| 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 |
A minimal deployment (1 task, us-east-1, no free tier):
| Component | Monthly |
|---|---|
| Fargate (1 task, 0.25 vCPU / 512 MB) | ~$9 |
| Application Load Balancer | ~$22 |
| CloudWatch Logs | <$1 |
| Route53 | $0.50 |
| Total | ~$33/month |
See Fargate pricing and ALB pricing.
| Output | Description |
|---|---|
LoadBalancerDNS |
ALB DNS name |
Route53Domain |
Custom domain URL (only if domain is configured) |
npx cdk destroy --app "npx tsx stack/dev.ts" --profile default- fargate-nixpacks.md - Auto-generate Dockerfiles with Nixpacks
- fargate-full.md - Full configuration reference