Deploy AI Deploy to AWS using CDK. This guide covers everything from initial setup to a running production environment.
| Tool | Version | Install |
|---|---|---|
| AWS CLI | v2 | aws.amazon.com/cli |
| Node.js | 22+ | nodejs.org |
| AWS CDK CLI | latest | npm install -g aws-cdk |
| Python | 3.12+ | python.org |
| uv | latest | curl -LsSf https://astral.sh/uv/install.sh | sh |
| pnpm | 9+ | npm install -g pnpm |
| Docker | latest | docker.com — required for Lambda worker images |
aws configure
# AWS Access Key ID: <your-key>
# AWS Secret Access Key: <your-secret>
# Default region name: us-east-1 # must match your Bedrock-enabled regionVerify access:
aws sts get-caller-identity- Open the Bedrock console in your target region
- Go to Model access → Manage model access
- Request access to:
- Anthropic Claude Sonnet 4.5 (used by interview planner, design, IaC, and documentation agents)
- Anthropic Claude Haiku 4.5 (used by interview executor for fast single-turn processing)
- Wait for access status to show Access granted
Model access is region-specific. Ensure you request access in the same region you'll deploy to.
CDK needs a one-time bootstrap in each account/region:
export CDK_DEFAULT_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
export CDK_DEFAULT_REGION=us-east-1 # your target region
npx cdk bootstrap aws://$CDK_DEFAULT_ACCOUNT/$CDK_DEFAULT_REGIONThe frontend must be built as a static export before deployment:
cd frontend
pnpm install
NEXT_OUTPUT=export pnpm build # generates out/ directory
cd ..This creates frontend/out/ which CDK will deploy to S3 + CloudFront.
cd infra
npm install
npm run buildexport CDK_DEFAULT_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
export CDK_DEFAULT_REGION=us-east-1
npx cdk diffnpx cdk deploy \
-c environment=prod \
-c notificationEmail=your-ops-team@example.comCDK will show a summary of IAM changes and ask for confirmation. Type y to proceed.
First deployment takes 15-25 minutes (VPC, NAT gateways, ECS, CloudFront distribution).
| Key | Type | Default | Description |
|---|---|---|---|
environment |
string | dev |
Used in resource names and tags |
notificationEmail |
string | — | SNS subscription for CloudWatch alarms |
natGateways |
number | 2 | Number of NAT gateways (1 saves ~$32/mo) |
costCenter |
string | engineering |
Cost allocation tag |
owner |
string | platform-team |
Owner tag |
After deployment, CDK prints stack outputs. Save these values:
AiDeployStack.AlbDnsName = ai-deploy-xxxx.us-east-1.elb.amazonaws.com
AiDeployStack.FrontendUrl = https://d1234abcdef.cloudfront.net
AiDeployStack.DistributionId = E1234ABCDEF
AiDeployStack.WebSocketUrl = wss://abc123.execute-api.us-east-1.amazonaws.com/prod
AiDeployStack.UserPoolId = us-east-1_XXXXXXXXX
AiDeployStack.UserPoolClientId = xxxxxxxxxxxxxxxxxxxxxxxxxx
AiDeployStack.TableName = ai-deploy-table-prod
AiDeployStack.KnowledgeBaseBucketName = ai-deploy-s3-knowledgebase-xxxx
AiDeployStack.ArtifactsBucketName = ai-deploy-s3-artifacts-xxxx
You can also retrieve these later:
aws cloudformation describe-stacks --stack-name AiDeployStack --query 'Stacks[0].Outputs' --output tableThe static frontend is already deployed to CloudFront. However, it needs to know the backend URL.
Update frontend/.env.local with production values and rebuild:
NEXT_PUBLIC_BACKEND_URL=https://<AlbDnsName>
NEXT_PUBLIC_WEBSOCKET_URL=<WebSocketUrl>Then rebuild and redeploy:
cd frontend
NEXT_OUTPUT=export pnpm build
cd ../infra
npx cdk deploy # BucketDeployment uploads the new buildTo invalidate CloudFront cache after redeployment:
aws cloudfront create-invalidation \
--distribution-id <DistributionId> \
--paths "/*"Cognito is configured with email sign-in and MFA. Create a user via the AWS CLI:
USER_POOL_ID=<UserPoolId from outputs>
# Create user
aws cognito-idp admin-create-user \
--user-pool-id $USER_POOL_ID \
--username user@example.com \
--user-attributes \
Name=email,Value=user@example.com \
Name=email_verified,Value=true \
Name=custom:tenant_id,Value=team-alpha \
--temporary-password 'TempPass123!'
# Set permanent password (skip forced change)
aws cognito-idp admin-set-user-password \
--user-pool-id $USER_POOL_ID \
--username user@example.com \
--password 'YourSecurePassword123!' \
--permanentThe custom:tenant_id attribute controls data isolation — users in different tenants cannot see each other's projects.
The platform works without a Knowledge Base, but design quality improves significantly with product reference documentation.
KB_BUCKET=<KnowledgeBaseBucketName from outputs>
aws s3 cp product-admin-guide.pdf s3://$KB_BUCKET/
aws s3 cp product-best-practices.pdf s3://$KB_BUCKET/- Open Bedrock console → Knowledge bases → Create
- Name:
ai-deploy-product-kb - Data source: S3, select the
KnowledgeBaseBucketNamebucket - Embedding model: Titan Embeddings V2
- Vector store: let Bedrock create an OpenSearch Serverless collection (default)
- Create and sync
After creation, note the Knowledge Base ID and update the ECS task environment:
# Update the ECS task definition with the KB ID
aws ecs update-service \
--cluster ai-deploy-cluster-prod \
--service <ServiceName> \
--force-new-deployment \
--task-definition $(
aws ecs describe-services \
--cluster ai-deploy-cluster-prod \
--services <ServiceName> \
--query 'services[0].taskDefinition' \
--output text
)Or set the env var in the CDK stack by adding to ai-deploy-stack.ts:
ecsConstruct.service.taskDefinition.defaultContainer?.addEnvironment(
"AI_DEPLOY_KNOWLEDGE_BASE_ID",
"YOUR_KB_ID",
);Then redeploy: npx cdk deploy
ALB_DNS=<AlbDnsName>
# Basic
curl https://$ALB_DNS/ping
# {"status": "ok"}
# Deep (checks Bedrock + DynamoDB connectivity)
# In production (debug=false), only returns {"status": "healthy"} or {"status": "degraded"}
# In debug mode, returns full dependency check details
curl https://$ALB_DNS/health
# {"status": "healthy"}Open the FrontendUrl from stack outputs in your browser. You should see the project dashboard.
The WebSocket is used for real-time task updates. Verify the endpoint is reachable:
wscat -c "<WebSocketUrl>"
# Connected| Resource | Purpose | Cost Estimate (us-east-1) |
|---|---|---|
| VPC (2-AZ, 2 NAT gateways) | Network isolation | ~$64/mo |
| ECS Fargate (2 vCPU, 4 GB, 2-10 tasks) | FastAPI backend | ~$70/mo |
| ALB + WAF | Load balancing + security | ~$16/mo |
| DynamoDB (on-demand) | Project metadata + task state | ~$10-50/mo |
| S3 (3 buckets) | KB docs, artifacts, access logs | ~$5/mo |
| Lambda (9 functions) | 3 workers + 6 WebSocket handlers | ~$1-5/mo |
| SQS (3 FIFO + 3 DLQ) | Async task queues | <$1/mo |
| CloudFront | Frontend CDN | ~$1-5/mo |
| Cognito | User authentication | Free tier |
| KMS | Encryption at rest | ~$1/mo |
| CloudWatch + SNS | Alarms + notifications | ~$5/mo |
| Total | ~$170-220/mo |
Set natGateways=1 to save ~$32/mo in non-production environments.
- Encryption at rest: KMS customer-managed key for DynamoDB, S3, CloudWatch, SNS, SQS
- Encryption in transit: TLS 1.2+ on ALB, HTTPS on CloudFront, SSL enforced on S3
- Network: ECS in private subnets, VPC endpoints for Bedrock/S3/DynamoDB, NAT for outbound
- WAF: AWS managed rule groups (common + known bad inputs) + IP rate limiting (1000 req/5min)
- Auth: Cognito with MFA, 12-char password policy, SRP auth only
- Headers: HSTS, X-Frame-Options DENY, nosniff, strict referrer policy
- Compliance: cdk-nag (AWS Solutions) checks enabled, suppressions documented in code
# Rebuild backend Docker image + redeploy
cd infra && npx cdk deploy
# Frontend changes only
cd frontend && NEXT_OUTPUT=export pnpm build
cd ../infra && npx cdk deploy
aws cloudfront create-invalidation --distribution-id <DistributionId> --paths "/*"To change Bedrock models, update the ECS container environment via CDK or directly:
# In infra/lib/ecs.ts, update the model ID environment variables, then:
cd infra && npx cdk deploycd infra
npx cdk destroyThe Knowledge Base S3 bucket has RETAIN removal policy — it won't be deleted on stack destroy. Delete manually if needed:
aws s3 rb s3://<KnowledgeBaseBucketName> --forceCheck container logs:
aws logs tail /ecs/ai-deploy-prod --followCommon causes: missing env vars, Bedrock access not granted, incorrect region.
The backend circuit breaker trips after repeated Bedrock failures. Check:
- Bedrock model access is granted in the correct region
- The ECS task role has
bedrock:InvokeModelpermission (CDK sets this automatically) - Bedrock isn't experiencing an outage
The CDK stack auto-configures AI_DEPLOY_CORS_ORIGINS to the CloudFront domain. If using a custom domain, add it via CDK context or update the ECS environment variable.
Check the dead-letter queues for failed tasks:
aws sqs receive-message --queue-url <DesignDlqUrl> --max-number-of-messages 1CloudWatch alarms will fire when DLQ depth exceeds thresholds.
Default timeout is 15 minutes. If IaC generation consistently times out, the Bedrock model may be under heavy load. Check Lambda CloudWatch logs and consider using a faster model.