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

Request path for hybridcloudgrid.com A visitor's browser resolves the domain through Route 53, connects to CloudFront over TLS with an ACM certificate, and CloudFront sends a signed request to a Lambda function URL. Lambda runs a container image pulled from ECR. Visitorbrowser Route 53DNS alias CloudFrontTLS · cache · edge ACMcertificate Lambda URLIAM auth · signed Containeradapter + FastAPI ECRimmutable images
Every box above is created by Terraform. Nothing was clicked together in the console.
  1. Route 53 answers DNS for the domain. Alias records point both the bare domain and www at CloudFront, which a plain CNAME cannot do at the root of a domain.
  2. CloudFront terminates HTTPS with a free certificate from ACM, redirects plain HTTP to HTTPS and rejects old TLS versions.
  3. 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.
  4. 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 /tmp is writable, so anything that must persist goes to S3 or a database.

What it costs

ItemMonthly cost at low traffic
Route 53 hosted zone$0.50
Lambda requests and computeWithin the always-free allowance
CloudFrontWithin the always-free allowance
ACM certificateFree
ECR image storage (last 10 images kept)Cents
CloudWatch logs (14-day retention)Cents
Terraform state in S3Cents
Domain nameBilled 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.