All projects
ProjectIn progress

This site

Can a blog be its own first cloud project?

Started
October 2026
Stack
Astro, Terraform, AWS, S3, CloudFront, WAF, Route 53, GitHub Actions, CodeBuild
Source
github.com/deployresponsibly/cloud-blog
Last updated
Oct 4, 2026

Overview

A typical “dev” blog, and my attempt at getting my thoughts and learnings down on record. I’ve made attempts at this before and it’s always been left behind. I don’t write particularly well, and my actual job keeps me busy. But we’re giving this a go one more time.

This blog’s deployment pattern is your typical S3 static website. It’s an Astro site served from a private S3 bucket through CloudFront via OAC. Terraform defines the whole stack, a pull request runs the checks, and a push to main deploys.

Architecture

GitHub repo
Astro build
CodeBuild runner
S3 bucketCloudFront + WAF
Route 53
Reader
  • GitHub repo connects to Astro build (push to main)
  • Astro build connects to CodeBuild runner (artifact)
  • CodeBuild runner connects to S3 bucket (s3 sync)
  • Reader connects to Route 53 (lookup)
  • Reader connects to CloudFront + WAF (request)
  • CloudFront + WAF connects to S3 bucket (origin)
FIG. 1 How a post gets from a Markdown file to a reader. Builds run on GitHub; only the deploy job holds AWS credentials. Boxes marked with a ring open the post that covers them.

Posts

  1. Part 1Oct 4, 2026

    Choosing the stack

    Why this blog is Astro on S3 and CloudFront, built with Terraform, and the one resource I could not find anywhere on Google.

  2. Part 2Oct 4, 2026

    Hosting on S3 and CloudFront

    A private S3 bucket behind CloudFront with Origin Access Control, and the small function that makes directory-style URLs work.

  3. Part 4Next

    The deploy pipeline

  4. Part 5Planned

    What broke along the way

Decisions

What I chose, and why
  1. 01Chose a static site over a CMS, so every page is a file in the repo and there is no server to run.
  2. 02Kept the S3 bucket private and put CloudFront in front of it with Origin Access Control, so the only way to read the site is through the CDN.
  3. 03Used CloudFront's Free flat-rate plan. It includes a WAF but allows only five rules, managed response headers and no access logs, so the design fits inside those limits.
  4. 04Left DNS in the AWS account that already owns the domain and reached it from the site account through a second Terraform provider.
  5. 05Stored Terraform state in S3 with native locking instead of adding a DynamoDB table.
  6. 06Split the pipeline in two. GitHub builds the site with no AWS access; a CodeBuild-hosted runner deploys the finished artifact using a role scoped to one bucket and one distribution.
  7. 07Pinned every GitHub Action to a commit SHA, and made GitHub reject any that are not.