Astro instead of a client-only React SPA
The site is mostly static content, so Astro keeps the output SEO-friendly by generating real HTML instead of an empty JavaScript-rendered root.
Static portfolio hosted on AWS with an Astro build, private S3 origin, CloudFront distribution, Origin Access Control, ACM TLS, Terraform, and manually managed Cloudflare DNS.
Overview
The first phase focused on replacing ad hoc static hosting with a reproducible AWS deployment path. The site previously ran on a resilient homelab Kubernetes cluster with over 90 days of uptime, UPS-backed power, and redundant internal paths, but the public edge was still limited by the ISP router and uplink. That dependency was the bottleneck I could not realistically bypass from home.
For a DevOps-focused portfolio, that made the hosting story part of the signal, so the move to AWS gives the site a more reliable public edge. At the moment it also runs at near-zero cost because the architecture uses static hosting, CDN caching, and managed AWS services efficiently.
This site is also intended to grow as I practice and learn more AWS services, so each phase can showcase more of the surrounding cloud architecture. The hosting stack now models production concerns: private origin access, TLS, CDN caching, infrastructure as code, and a repeatable deploy script.
Phase 1
Astro static build with real HTML output for fast loading and SEO.
Private S3 bucket with public access blocked and server-side encryption.
CloudFront with OAC, HTTPS redirect, ACM certificate, and managed caching.
Cloudflare records configured manually. The domain also supports mail and homelab Kubernetes apps, so DNS remains centralized outside AWS instead of moving to Route 53.
Scripted build, S3 sync, cache headers, and CloudFront invalidation.
Key decisions
The site is mostly static content, so Astro keeps the output SEO-friendly by generating real HTML instead of an empty JavaScript-rendered root.
The bucket is not public. CloudFront is the only intended read path, which keeps access control centralized at the CDN layer.
OAC signs origin requests with SigV4 and replaces older public-bucket or Origin Access Identity patterns.
Terraform manages the AWS side while DNS records stay in Cloudflare. The domain is already used for mail and homelab Kubernetes apps, and Cloudflare is free for this use case, so Route 53 would add cost and another DNS control plane without clear benefit.
The entry document uses a short TTL so updates appear quickly. Non-HTML build assets can be cached for much longer because their filenames are generated by the build.
Deployment flow
Next phases