← All projects

AWS cloud homelab

The client portal of Harbor Point Accounting, a fictional 20-person firm, moved from an office server to AWS and built with Terraform. I built it, broke it on purpose and fixed it the way a cloud support engineer would.

Type
Personal learning lab
Built
October 2026
Stack
AWS, Terraform, Python, Bash, Docker
Running cost
About US$0.10 an hour, destroyed after every session
Architecture diagram of the AWS homelab
Solid lines are network traffic. Dashed lines are AWS API calls made with the servers’ instance role. Account-level guards (a US$5 budget alert and CloudTrail) are not drawn. Open the image for full size.

What I built

  • Network. One VPC across two availability zones. Public subnets hold the load balancer and web servers; private subnets for the database have no route to the internet.
  • Web tier. Two Amazon Linux 2023 servers running nginx in front of a small Python app, in an Auto Scaling group behind an Application Load Balancer. A server that fails its health check is replaced automatically.
  • Database. RDS PostgreSQL 16 in the private subnets. Only the web servers’ security group can reach port 5432, and the password lives in SSM Parameter Store as a SecureString, never in the code.
  • Backups. A versioned, encrypted S3 bucket. The servers’ IAM role can upload and read backups but cannot delete them.
  • Access. No SSH and no open port 22. I connect to the servers through Session Manager.
  • Monitoring. The CloudWatch agent ships memory, disk and nginx logs. Four alarms (CPU, unhealthy targets, load balancer 5xx and disk) email me through SNS, and a dashboard shows it all in one place.
  • Automation. A Python (boto3) script checks the account for risky settings, such as a security group open to the internet. The app also runs as a Docker image.
  • CI. GitHub Actions runs terraform fmt and validate on every push, with no AWS credentials stored in GitHub.

Proof from my account

Every screenshot is from my own AWS account. Select one to open it full size.

What broke, and how I fixed it

This account turned out to be one of AWS’s new-style accounts: one fixed Region, no root sign-in and guardrails set by AWS. The guide didn’t expect that, so a few things broke. I logged nine problems in total; these are the ones worth reading.

SymptomCauseFix
Budget alert email stuck at Inactive; verifying it was deniedA service control policy above my account blocks email-recipient verificationSent the alerts through an SNS topic instead. Write-up
First terraform apply failed: servers wouldn’t launchThe new IAM role hadn’t reached EC2 yetterraform untaint, no rebuild. Write-up
Policy simulator said explicitDeny for an upload that workedThe Region rule above the account; the simulated request had no RegionAdded the Region as context. Write-up
Terraform found no credentials in a new terminalThe profile environment variable lasts one windowSet it at the start of every session

What production would do differently

Some choices were made to keep the lab cheap. I wrote down what a real deployment would change:

This labProductionWhy the lab differs
Web servers in public subnets, locked down by security groupsPrivate subnets plus a NAT Gateway or VPC endpointsA NAT Gateway costs about US$43 a month in Sydney
Single-AZ database, 1-day backupsMulti-AZ, longer retention, deletion protectionDoubles the database cost
HTTP on the load balancer’s own nameHTTPS with ACM and a Route 53 domainNeeds a paid domain
Local Terraform stateS3 backend with lockingOne person, one environment

This is a learning lab, not production experience. The guide and code were written with AI assistance as a guided walkthrough; I set up the environment, ran every step myself, troubleshot what broke and wrote up what I found.