You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Architecture for a Go binary web service with PostgreSQL database, scaling from 5 to hundreds of users over a year. Optimized for operational simplicity and fast iteration.
Why CDK (TypeScript) for Infrastructure
Factor
CDK
CloudFormation
AWS CLI
AI implementation ease
⭐⭐⭐ Best
⭐⭐ Medium
⭐ Poor
Error detection
Compile-time
Deploy-time
Runtime
Refactoring
Easy
Copy-paste
Manual
Single deploy command
cdk deploy
Multi-step
Many calls
CDK advantages for this project:
Type safety catches errors before deployment
Mature L2 constructs for RDS (DatabaseInstance)
App Runner alpha construct (@aws-cdk/aws-apprunner-alpha) works well
Source.fromAsset() builds container directly from local Dockerfile
import*ascdkfrom'aws-cdk-lib';import*asec2from'aws-cdk-lib/aws-ec2';import*asrdsfrom'aws-cdk-lib/aws-rds';import*assecretsmanagerfrom'aws-cdk-lib/aws-secretsmanager';import*asapprunnerfrom'@aws-cdk/aws-apprunner-alpha';import{Construct}from'constructs';import*aspathfrom'path';exportclassAppStackextendscdk.Stack{constructor(scope: Construct,id: string,props?: cdk.StackProps){super(scope,id,props);// VPC with private subnets for RDSconstvpc=newec2.Vpc(this,'AppVpc',{maxAzs: 2,natGateways: 1,// Reduce to 0 for cost savings if App Runner doesn't need outbound});// Database credentials in Secrets ManagerconstdbSecret=newsecretsmanager.Secret(this,'DbSecret',{generateSecretString: {secretStringTemplate: JSON.stringify({username: 'postgres'}),generateStringKey: 'password',excludePunctuation: true,},});// RDS PostgreSQL instanceconstdatabase=newrds.DatabaseInstance(this,'Database',{engine: rds.DatabaseInstanceEngine.postgres({version: rds.PostgresEngineVersion.VER_16,}),instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G,ec2.InstanceSize.MICRO,// Upgrade to SMALL as needed),
vpc,vpcSubnets: {subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS},credentials: rds.Credentials.fromSecret(dbSecret),allocatedStorage: 20,maxAllocatedStorage: 100,databaseName: 'appdb',removalPolicy: cdk.RemovalPolicy.SNAPSHOT,// Change to DESTROY for dev});// VPC Connector for App Runner to reach RDSconstvpcConnector=newapprunner.VpcConnector(this,'VpcConnector',{
vpc,vpcSubnets: {subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS},securityGroups: [database.connections.securityGroups[0]],});// App Runner service from local Dockerfileconstservice=newapprunner.Service(this,'AppRunner',{source: apprunner.Source.fromAsset({imageConfiguration: {port: 8080,environmentVariables: {DB_HOST: database.dbInstanceEndpointAddress,DB_PORT: database.dbInstanceEndpointPort,DB_NAME: 'appdb',},environmentSecrets: {DB_USER: apprunner.Secret.fromSecretsManager(dbSecret,'username'),DB_PASSWORD: apprunner.Secret.fromSecretsManager(dbSecret,'password'),},},asset: newcdk.aws_ecr_assets.DockerImageAsset(this,'GoAppImage',{directory: path.join(__dirname,'../../app'),}),}),
vpcConnector,cpu: apprunner.Cpu.ONE_VCPU,memory: apprunner.Memory.TWO_GB,autoDeploymentsEnabled: true,});// Allow App Runner to connect to RDSdatabase.connections.allowFrom(vpcConnector,ec2.Port.tcp(5432));// Outputsnewcdk.CfnOutput(this,'ServiceUrl',{value: `https://${service.serviceUrl}`,});newcdk.CfnOutput(this,'DbEndpoint',{value: database.dbInstanceEndpointAddress,});}}
app/Dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main .
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
Deployment Commands
# Initial setup (one time)cd infra
npm install
cdk bootstrap
# Deploy everything
cdk deploy
# After Go code changes - rebuild and pushcd ../app
docker build -t my-app .# If auto-deploy enabled, App Runner picks up new image automatically# Destroy (cleanup)
cdk destroy
Scaling Path
Users
Compute
Database
Est. Monthly Cost
5-50
App Runner (1 instance)
db.t4g.micro
~$30-50
50-200
App Runner (2-4 auto)
db.t4g.small
~$80-150
200-500
App Runner (4-8 auto)
db.t4g.medium + Multi-AZ
~$200-400
Verification Steps
cdk deploy completes without errors
Access the ServiceUrl output - verify HTTPS works
Test database connectivity from the Go app
Check CloudWatch logs for App Runner service
Optional: Load test with k6 or hey to verify auto-scaling