AWS Infrastructure
CivStart's backend infrastructure is deployed to AWS using Terraform. This guide covers the complete setup, deployment, and management of AWS resources.
Architecture Overview
Infrastructure Components
- ECS Fargate: Serverless container orchestration for backend API
- RDS PostgreSQL: Managed PostgreSQL database (version 16)
- ElastiCache Redis: Managed Redis cache
- Application Load Balancer (ALB): Traffic distribution and health checks
- VPC: Isolated network with public/private subnets across 3 AZs
- ECR: Docker container registry
- Secrets Manager: Secure storage for environment variables
- CloudWatch: Logging and monitoring
Cost Estimates
| Environment | Monthly Cost | Notes |
|---|---|---|
| Dev | $50-70 | Single NAT, t3.micro instances, single AZ |
| Staging | $50-70 | Single NAT, t3.micro instances, single AZ |
| Production | $150-200 | Single NAT, t3.small instances, Multi-AZ RDS |
| Total | $250-340 | Cost-optimized for small to medium workloads |
Prerequisites
- AWS Account with appropriate permissions
- Terraform >= 1.5.0 (Install)
- AWS CLI configured (Install)
- Domain names (optional, for custom domains)
Directory Structure
terraform/
├── modules/ # Reusable Terraform modules
│ ├── networking/ # VPC, subnets, security groups
│ ├── rds/ # PostgreSQL database
│ ├── elasticache/ # Redis cache
│ ├── ecr/ # Container registry
│ ├── alb/ # Load balancer
│ ├── ecs-backend/ # ECS cluster & services
│ └── secrets/ # Secrets Manager
├── environments/ # Environment-specific configurations
│ ├── dev/
│ ├── staging/
│ └── production/
├── main.tf # Main infrastructure definition
├── variables.tf # Input variables
├── outputs.tf # Output values
├── provider.tf # AWS provider configuration
└── backend.tf # Terraform state backend
Initial Setup
Step 1: Create Terraform State Backend
# Get your AWS account ID
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
# Create S3 bucket for Terraform state
aws s3api create-bucket \
--bucket civstart-terraform-state-${AWS_ACCOUNT_ID} \
--region us-east-1
# Enable versioning
aws s3api put-bucket-versioning \
--bucket civstart-terraform-state-${AWS_ACCOUNT_ID} \
--versioning-configuration Status=Enabled
# Enable encryption
aws s3api put-bucket-encryption \
--bucket civstart-terraform-state-${AWS_ACCOUNT_ID} \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}]
}'
# Block public access
aws s3api put-public-access-block \
--bucket civstart-terraform-state-${AWS_ACCOUNT_ID} \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
# Create DynamoDB table for state locking
aws dynamodb create-table \
--table-name civstart-terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1
Step 2: Configure Environment Variables
cd terraform
# Copy example files
cp environments/dev/terraform.tfvars.example environments/dev/terraform.tfvars
cp environments/dev/backend.tfvars.example environments/dev/backend.tfvars
# Edit with your values (never commit these files)
vim environments/dev/terraform.tfvars
vim environments/dev/backend.tfvars
Step 3: Initialize Terraform
# For dev environment
terraform init -backend-config=environments/dev/backend.tfvars
# For staging
terraform init -backend-config=environments/staging/backend.tfvars -reconfigure
# For production
terraform init -backend-config=environments/production/backend.tfvars -reconfigure
Deployment
Deploy Dev Environment
# Plan (review changes)
terraform plan -var-file=environments/dev/terraform.tfvars
# Apply (create infrastructure)
terraform apply -var-file=environments/dev/terraform.tfvars
# Save outputs
terraform output > outputs-dev.txt
Post-Deployment: Push Docker Image
# Get ECR repository URL from Terraform output
ECR_URL=$(terraform output -raw ecr_repository_url)
AWS_REGION="us-east-1"
# Login to ECR
aws ecr get-login-password --region $AWS_REGION | \
docker login --username AWS --password-stdin $ECR_URL
# Build and push backend image
docker build -f apps/backend/Dockerfile -t $ECR_URL:latest .
docker push $ECR_URL:latest
Update ECS Service
# Force new deployment with the image
CLUSTER_NAME=$(terraform output -raw ecs_cluster_name)
SERVICE_NAME=$(terraform output -raw ecs_service_name)
aws ecs update-service \
--cluster $CLUSTER_NAME \
--service $SERVICE_NAME \
--force-new-deployment
Management Commands
View Logs
LOG_GROUP=$(terraform output -raw cloudwatch_log_group)
# View recent logs
aws logs tail $LOG_GROUP --follow
# View logs from specific time
aws logs tail $LOG_GROUP --since 1h --follow
Scale Service
CLUSTER_NAME=$(terraform output -raw ecs_cluster_name)
SERVICE_NAME=$(terraform output -raw ecs_service_name)
# Scale to 3 tasks
aws ecs update-service \
--cluster $CLUSTER_NAME \
--service $SERVICE_NAME \
--desired-count 3
Database Operations
# Create manual snapshot
RDS_INSTANCE="civstart-dev-postgres"
aws rds create-db-snapshot \
--db-instance-identifier $RDS_INSTANCE \
--db-snapshot-identifier $RDS_INSTANCE-$(date +%Y%m%d-%H%M%S)
Troubleshooting
ECS Task Not Starting
# Check service events
aws ecs describe-services \
--cluster $CLUSTER_NAME \
--services $SERVICE_NAME \
--query 'services[0].events[:10]'
# Check task logs
aws logs tail $LOG_GROUP --since 30m
Health Check Failing
TARGET_GROUP=$(terraform output -raw target_group_arn)
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP
Database Connection Issues
- Verify security group rules allow ECS to RDS traffic
- Check database is in the same VPC as ECS
- Verify DATABASE_URL in Secrets Manager is correct
Cleanup
danger
Destroying infrastructure will delete all data. Use with extreme caution.
# Dev environment
terraform destroy -var-file=environments/dev/terraform.tfvars
Security Best Practices
- Enable MFA for AWS root account
- Use IAM roles instead of access keys where possible
- Rotate secrets regularly (Clerk keys, API keys)
- Enable AWS CloudTrail for audit logging
- Set up AWS Config for compliance monitoring
- Enable GuardDuty for threat detection (production)
Cost Optimization
- Use single NAT Gateway for dev/staging (already configured)
- Stop RDS during non-business hours (dev only)
- Use Reserved Instances for production (40-60% savings)
- Enable RDS Multi-AZ only for production
- Use smaller instance types for dev environment