Blogging Setup: Part 2, Github Action and AWS S3

2024/11/30

BUILDgithub-actionsinfra

Part 1 got us a blog that works on localhost. Useful for exactly one person. Now we make it public – S3 for hosting, GitHub Actions for deployment. Push to main, site updates. No manual steps.

Set up S3

Create a bucket

Pick a unique bucket name (e.g., my-blog-bucket) and a region. Nothing special here.

Make it publicly readable

Go to your bucket’s Permissions tab, find Bucket Policy, and add:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}

Replace your-bucket-name with your actual bucket name.

Enable static website hosting

In the Properties tab, scroll to Static Website Hosting and enable it. Set the index document to index.html.

You’ll get a bucket website endpoint – something like http://your-bucket-name.s3-website-us-west-1.amazonaws.com. That’s your blog’s URL. It’s not pretty, but it works. Add a custom domain later if vanity matters.

Set up GitHub Actions

Connect your repo

git remote add origin <your-repo-url>

Add secrets

Go to Settings > Secrets and variables > Actions in your repo. Add three secrets:

Secret Value
AWS_S3_BUCKET Your bucket name
AWS_ACCESS_KEY_ID Your AWS access key
AWS_SECRET_ACCESS_KEY Your AWS secret key

Create the workflow

Add .github/workflows/deploy.yml:

name: Upload Website

on:
  push:
    branches:
    - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - uses: jakejarvis/s3-sync-action@master
      with:
        args: --acl public-read --follow-symlinks --delete
      env:
        AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        AWS_REGION: 'us-west-1'   # optional: defaults to us-east-1
        SOURCE_DIR: 'public'      # optional: defaults to entire repository

This syncs your Hugo public/ directory to S3 on every push to master. The --delete flag removes files from S3 that no longer exist locally, so your bucket stays clean.

The full loop

Write in Org-mode. Export with ox-hugo. Commit and push. GitHub Actions builds and syncs to S3. Your site is live.

The entire deploy is a git push. Which is exactly how it should be.