This website is a small production system: a custom domain, TLS, a CDN, containerized application code and infrastructure defined entirely in Terraform. It costs roughly a dollar or two a month to run, plus the domain name. Here is how it fits together and why each piece was chosen.
The architecture
- Route 53 answers DNS for the domain. Alias records point both the bare domain and
wwwat CloudFront, which a plain CNAME cannot do at the root of a domain. - CloudFront terminates HTTPS with a free certificate from ACM, redirects plain HTTP to HTTPS and rejects old TLS versions.
- CloudFront forwards the request to a Lambda function URL, signing it with Origin Access Control. The function URL only accepts signed requests, so the origin cannot be reached around the CDN.
- Lambda runs a container image stored in ECR. Inside it, a small adapter translates Lambda events into ordinary HTTP for a standard Python web application.
Why Lambda and not a container service
The obvious way to run a container on AWS is ECS on Fargate. For a low-traffic site, it has one expensive problem: it does not scale to zero, and it needs a load balancer in front, which bills every hour whether anyone visits or not. Lambda bills per request, and its free allowance of one million requests a month covers a site like this entirely.
The Lambda Web Adapter makes this painless. The application is an ordinary web app listening on a port. The same image runs unchanged with docker run on a laptop, on Kubernetes, or on Lambda. If the site ever outgrows Lambda, moving it is a deployment change, not a rewrite.
The trade-offs are real, and worth knowing:
- Cold starts. After a quiet period, the first request waits a second or two while a container starts.
- Request limits. A single request can run for at most 15 minutes, and long-lived connections need other services.
- Read-only filesystem. Only
/tmpis writable, so anything that must persist goes to S3 or a database.
What it costs
| Item | Monthly cost at low traffic |
|---|---|
| Route 53 hosted zone | $0.50 |
| Lambda requests and compute | Within the always-free allowance |
| CloudFront | Within the always-free allowance |
| ACM certificate | Free |
| ECR image storage (last 10 images kept) | Cents |
| CloudWatch logs (14-day retention) | Cents |
| Terraform state in S3 | Cents |
| Domain name | Billed yearly by the registrar |
Two small settings keep it that way: an ECR lifecycle policy that deletes old images, and a fixed retention period on the log group. Left at their defaults, both would grow forever.
How changes are made
- Infrastructure lives in Terraform, with state in an encrypted, versioned S3 bucket that uses native state locking.
- Every image is tagged with the git commit it was built from, and the image repository refuses to overwrite tags. What is running always maps to exactly one commit, and rolling back means pointing at an older tag.
- Terraform owns the shape, the deploy step owns the version. Terraform is told to ignore the running image, so applying infrastructure changes can never quietly roll the site back.
What this means for your systems
Most small products do not need a cluster, a load balancer and a monthly bill to match. They need the right-sized option, built in code, with a clear path to grow. That is the same approach we take for clients. If you want to know whether your setup is sized right, a cloud review is a good place to start.