Skip to main content

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

EnvironmentMonthly CostNotes
Dev$50-70Single NAT, t3.micro instances, single AZ
Staging$50-70Single NAT, t3.micro instances, single AZ
Production$150-200Single NAT, t3.small instances, Multi-AZ RDS
Total$250-340Cost-optimized for small to medium workloads

Prerequisites

  1. AWS Account with appropriate permissions
  2. Terraform >= 1.5.0 (Install)
  3. AWS CLI configured (Install)
  4. 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

  1. Verify security group rules allow ECS to RDS traffic
  2. Check database is in the same VPC as ECS
  3. 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

  1. Enable MFA for AWS root account
  2. Use IAM roles instead of access keys where possible
  3. Rotate secrets regularly (Clerk keys, API keys)
  4. Enable AWS CloudTrail for audit logging
  5. Set up AWS Config for compliance monitoring
  6. Enable GuardDuty for threat detection (production)

Cost Optimization

  1. Use single NAT Gateway for dev/staging (already configured)
  2. Stop RDS during non-business hours (dev only)
  3. Use Reserved Instances for production (40-60% savings)
  4. Enable RDS Multi-AZ only for production
  5. Use smaller instance types for dev environment