The 5-Minute Tax I Killed With GitHub Actions
How I eliminated the 5-minute deployment tax with GitHub Actions so git push just works
Every time I finished writing a blog post, I had to do this:
cd sites/graycloudarch
hugo --minify
aws s3 sync public/ s3://graycloudarch-website --delete
aws cloudfront create-invalidation --distribution-id E1234ABCDEF --paths "/*"
Five minutes. Doesn’t sound like much.
But when you’re trying to publish 2-3 posts per week while working full-time, those 5 minutes add up. Not just in time—in friction.
“I just finished writing. Now I need to context-switch to deployment mode. What was that CloudFront ID again?”
Friction kills momentum.
What I Wanted
git push → site updates automatically → I move on to the next thing.
Zero thinking. Zero context switching. Zero “oh crap, I forgot to invalidate CloudFront.”
The Solution: GitHub Actions
GitHub Actions can build and deploy your site every time you push to main. For free.
Here’s the whole workflow:
name: Deploy graycloudarch.com
on:
push:
branches: [main]
paths:
- 'sites/graycloudarch/**'
- 'content/graycloudarch/**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: peaceiris/actions-hugo@v2
with:
hugo-version: 'latest'
extended: true
- name: Build site
working-directory: ./sites/graycloudarch
run: hugo --minify
- name: Configure AWS
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy
working-directory: ./sites/graycloudarch
run: |
aws s3 sync public/ s3://graycloudarch-website --delete
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION }} \
--paths "/*"
That’s it. Push to main, GitHub Actions handles the rest.
The Part That Tripped Me Up
Hugo themes are usually Git submodules. If you don’t check them out, your build fails with cryptic errors about missing layouts.
- uses: actions/checkout@v4
with:
submodules: true # Don't forget this
Cost me 20 minutes of debugging before I realized. Now it’s documented in code, not lost in my bash history.
Path Filtering: The Secret Sauce
I run two sites in one repo: graycloudarch.com and cloudpatterns.io.
Without path filtering, every push rebuilds both sites, even if I only changed one. Wasted build minutes, unnecessary CloudFront invalidations, slower feedback.
on:
push:
paths:
- 'sites/graycloudarch/**'
- 'content/graycloudarch/**'
Now GitHub Actions only runs when files for that site change. Fast, efficient, no waste.
Why This Matters
I’m trying to hit $3K/month by March 31. That’s 9 weeks.
Every minute I spend deploying is a minute I’m not writing, not reaching out to clients, not building the course I want to sell.
Manual deployments are a tax on my time. This workflow eliminated that tax.
Now when I finish writing, I commit and push. Two minutes later, it’s live. I’m already working on the next post.
The Real Win
It’s not the 5 minutes per deployment.
It’s the mental overhead.
Before: “Okay, post is done. Now I need to switch gears, build Hugo, sync to S3, remember that CloudFront command…”
After: “Post is done. git push. What’s next?”
No context switch. No friction. Just ship and move on.
That’s worth way more than 5 minutes.
Want to set this up for your site? The workflow above works for any Hugo + S3 + CloudFront setup. Just plug in your bucket names and distribution IDs in GitHub Secrets.
Or reach out if you want help automating your deployments. I do this for a living.
Working through a similar problem?
Fractional infrastructure architecture — 10–20 hrs/week for 3–6 months. No full-time headcount required.