This directory contains the complete Infrastructure as Code (Terraform) to deploy the Personal Lifecycle Tracker as a $0.00 / month forever, zero-server-maintenance serverless application on AWS with all traditional serverless downsides completely eliminated.
GLOBAL PUBLIC TRAFFIC
│
▼
AWS CloudFront (CDN Edge)
[Free: 1 TB/mo transfer + 10M requests forever]
│ │
/* (Static React UI) /api/* & /health
│ │
▼ ▼
AWS S3 Bucket AWS Lambda Function URL
[Free: 5 GB storage] (or HTTP API Gateway)
[Free: 1M reqs + 3.2M sec compute]
│
▼
Containerized FastAPI Backend
with AWS Lambda Web Adapter
(0 lines code changed)
│
▼
Neon Serverless PostgreSQL (Pooled)
[-pooler PgBouncer connection]
[Free: 0.5 GB storage + 0.5 CU]
▲
│
Ping every 4 minutes (SELECT 1)
│
AWS EventBridge Scheduler
[Free: 14M triggers/mo forever]
/health every 4 minutes. Both Lambda and Neon remain warm in memory 24/7 for $0.aws-lambda-adapter) in backend/Dockerfile. The exact same container runs locally in Docker Compose and in AWS Lambda with zero code rewrites.-pooler connection URI), multiplexing hundreds of concurrent Lambda requests.POST /api/v1/reminders/digest/send at 08:00 AM UTC with dedicated invocation time....-pooler.us-east-2.aws.neon.tech/lifecycle?sslmode=require).Validate configuration without host dependencies:
make tf-validate-serverless
Or manually:
docker run --rm -v $(pwd)/terraform/aws-serverless:/workspace -w /workspace hashicorp/terraform:1.7 init
docker run --rm -v $(pwd)/terraform/aws-serverless:/workspace -w /workspace hashicorp/terraform:1.7 validate
Copy the example configuration:
cp terraform/aws-serverless/terraform.tfvars.example terraform/aws-serverless/terraform.tfvars
Edit terraform/aws-serverless/terraform.tfvars with:
database_url: Your Neon pooled connection string.secret_key: Random 32-byte hex string (openssl rand -hex 32).docker run --rm -v $(pwd)/terraform/aws-serverless:/workspace -v $HOME/.aws:/root/.aws:ro \
-w /workspace -e AWS_DEFAULT_REGION=us-east-1 hashicorp/terraform:1.7 apply -auto-approve
Note the outputs:
ecr_repository_urls3_bucket_namecloudfront_domain_namelambda_function_url# Authenticate Docker with Amazon ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $(terraform -chdir=terraform/aws-serverless output -raw ecr_repository_url)
# Build and tag backend image with Lambda Web Adapter
docker build -t $(terraform -chdir=terraform/aws-serverless output -raw ecr_repository_url):latest ./backend
# Push to Amazon ECR
docker push $(terraform -chdir=terraform/aws-serverless output -raw ecr_repository_url):latest
# Update Lambda function code
aws lambda update-function-code \
--function-name personal-lifecycle-tracker-api-production \
--image-uri $(terraform -chdir=terraform/aws-serverless output -raw ecr_repository_url):latest
# Build static React 19 bundle
docker run --rm -v $(pwd)/frontend:/app -w /app node:20-alpine sh -c "npm install && npm run build"
# Sync build to S3
aws s3 sync frontend/dist s3://$(terraform -chdir=terraform/aws-serverless output -raw s3_bucket_name)/ --delete
Open your production CloudFront and Lambda endpoints:
# Verify Backend Health via Lambda Function URL
curl -s https://7ksomsylvke2uawuhhccbfr6hy0agzyb.lambda-url.ap-south-1.on.aws/health
# Expected Output: {"status":"healthy","database":"connected","version":"1.0.0"}
# Verify CloudFront CDN distribution
curl -sI https://d1ca9upoitnq9b.cloudfront.net/ | grep -E "HTTP|server|via"
# Expected Output: HTTP/2 200
Access the full web application at https://d1ca9upoitnq9b.cloudfront.net/. Pre-seeded demo credentials: alex@example.com / password123.