Stack
How this site is built, tested and deployed, and what the Terraform actually declares.
_Architecture

# Request pathRoute 53 resolves travispollard.com and www.travispollard.com to the CloudFront distribution via alias recordsCloudFront terminates TLS with an ACM certificate (SNI, TLS 1.2 minimum) and redirects any HTTP request to HTTPSCache misses on the default behavior fall through to the S3 static website origin holding the exported Next.js buildRequests under /cfb/data/* go to a second origin instead: the football pipeline bucket, reached through an Origin Access Control rather than a public website endpointThe visitor counter calls API Gateway, which invokes a Python Lambda that increments a DynamoDB item and returns the count# Commit to production1. A pull request runs the GitHub Actions gate: typecheck, lint, a full static export, and the Playwright suite on Chromium and Firefox2. That job pins TZ=UTC and the same Node version CodeBuild uses, because a gate running a different environment from the deploy has a gap in exactly the shape of the bug it is meant to catch3. A merge to main fires a webhook that starts CodePipeline within seconds4. CodeBuild installs with npm ci, builds the static export into frontend/out, and runs the same Playwright suite again5. The build artifact is deployed to the S3 bucket that backs the CloudFront distribution6. A final stage invalidates the distribution, so the change is visible without waiting out a TTLInfrastructure
| Resource | Configuration |
|---|---|
| S3 | Static website hosting for the exported Next.js build, read by CloudFront as a custom origin |
| CloudFront | Global CDN, TLS 1.2_2021 minimum, HTTP to HTTPS redirect, compression, PriceClass_100, plus a second origin for /cfb/data/* |
| Route 53 | Hosted zone with alias, MX, NS, and SOA records, plus ACM validation records |
| ACM | TLS certificate for the apex and www names, DNS validated through Route 53, in us-east-1 because CloudFront requires it |
| API Gateway + Lambda + DynamoDB | Visitor counter written in Python with boto3 |
| GitHub Actions | Pre-merge gate: typecheck, lint, static export, and Playwright on two browsers, pinned to UTC |
| CodePipeline + CodeBuild | Post-merge deploy: build and test per buildspec.yml, S3 deploy, then a CloudFront invalidation |
| SSM Parameter Store | The seam between this stack and the football pipeline: distribution id and ARN, so neither reads the other Terraform state |
| Terraform | S3, CloudFront, Route 53 and ACM are the modules s3, cloudfront, route53 and acm; the SSM parameters sit beside them. The visitor counter and the CodePipeline were built outside Terraform |
Writeup
I wrote about building this stack end to end, from an empty S3 bucket to a working CI/CD pipeline: From S3 to CI/CD: My Cloud Resume Challenge Journey
Source
The Terraform configuration and the Next.js frontend for this site live in one repository: github.com/jtravisp/travispollard.com