Goodbye Manual Deployments: How GitHub Actions Made AWS CI/CD Effortless ✨🚀

Originally published on Medium ↗

Goodbye Manual Deployments: How GitHub Actions Made AWS CI/CD Effortless ✨🚀

Photo by Ulysse Pointcheval on Unsplash

Every time I updated my AWS project manually, it felt like walking a tightrope 🎪 — small mistakes or missed steps would often lead to frustrating manual fixes and wasted time.

For context, I was maintaining a personal Single Page Application (SPA) hosted on AWS — a passion project that started to feel more like a tedious chore to deploy manually.

I knew there had to be a better way.
This is the story of how I finally embraced GitHub Actions and moved from chaotic manual deployments to smooth, automated CI/CD workflows — and why I wish I’d done it sooner.

The Struggles of Manual Deployments 😩

For a long time, deployment was a stressful checklist:

  • Build locally 🛠️
  • Upload to S3 Bucket 🪣️
  • Invalidate CloudFront cache ☁️
  • Pray nothing breaks 🙏

Every change, no matter how small, meant repeating the entire cycle.
Sometimes I’d forget a step. Sometimes the browser cache would refuse to refresh. Sometimes a deployment would quietly fail without me noticing.

One time, I accidentally uploaded an outdated static folder because I forgot to rebuild the assets. ⚠️
The result? A half-broken site that visitors started noticing.
It took me another two hours to manually diagnose and fix the issue — time I could have spent building new features.

Clearly, this wasn’t scalable.

Discovering GitHub Actions 🔍

I had heard of GitHub Actions before but assumed it would be complicated to set up.
Spoiler: It wasn’t.

After a few experiments (and a lot of trial and error) 🔄, I realized how powerful it could be for my needs.
Automating AWS deployments suddenly became not only possible — but surprisingly elegant.

And the more I automated, the more I realized:
Automation isn’t just about saving time — it’s about reducing cognitive load. 🧠
No more wondering “Did I forget to invalidate CloudFront?” or “Was that the latest version I uploaded?”.

Building the CI/CD Workflow 🛠️⚙️

I wanted a workflow that would:

  1. Detect a push to the main branch. ✅
  2. Build the latest React app 🔥
  3. Sync updated files to an S3 bucket. ☁️
  4. Invalidate CloudFront cache automatically. 🔄
  5. Update the prod Git tag to reflect the deployed commit. 🏷️

Here’s the heart of my GitHub Actions workflow:

name: Deploy to AWS  
  
on:  
  push:  
    branches:  
      - main  
  
jobs:  
  deploy:  
    runs-on: ubuntu-latest  
  
    steps:  
      - name: Checkout code  
        uses: actions/checkout@v3  
  
      - name: Setup Node.js  
        uses: actions/setup-node@v3  
        with:  
          node-version: '23.5.0'  
  
      - name: Cache node_modules  
        uses: actions/cache@v3  
        with:  
          path: ~/.npm  
          key: $-node-$  
          restore-keys: |  
            $-node-  
  
      - name: Install Dependencies  
        run: npm install  
  
      - name: Build React App  
        run: |  
          NODE_ENV=production npm run build  
  
      - name: Verify Build Output  
        run: test -d build && echo "✅ Build exists"  
  
      - name: Configure AWS credentials  
        uses: aws-actions/configure-aws-credentials@v2  
        with:  
          aws-access-key-id: $  
          aws-secret-access-key: $  
          aws-region: ap-southeast-1  
  
      - name: Sync files to S3  
        run: aws s3 sync ./build s3://your-bucket-name/ --delete  
  
      - name: Invalidate CloudFront cache  
        run: |  
          aws cloudfront create-invalidation --distribution-id ABC123XYZ --paths "/*"  
        
      - name: Update `prod` tag to current commit  
        run: |  
          git config user.name "github-actions"  
          git config user.email "github-actions@github.com"  
          git tag -f prod  
          git push origin prod --force

A quick breakdown:

  • Checkout code : Grabs the latest code from GitHub. 📦
  • Setup Node.js : Installs Node.js for building the app. 🔧
  • Cache node_modules : Speeds up future builds by reusing dependencies. ⚡
  • Install dependencies : Installs all required packages. 📦
  • Build React App : Compiles the production build. 🏗️
  • Verify build output : Ensures the build/ directory was created successfully. ✅
  • Configure AWS credentials : Authenticates the workflow securely. 🔐
  • Sync to S3 : Uploads the build/ directory to S3. ☁️
  • CloudFront invalidation : Forces the cache to refresh after deployment. 🔄
  • Update prod Git tag : Tags the deployed commit for easy production tracking. 🏷️

Lessons Learned Along the Way ✏️

Setting up CI/CD wasn’t without its lessons:

  • Secrets management matters : Store AWS credentials securely using GitHub Secrets. 🔒
  • Test small first : Start by automating just the S3 sync before adding cache invalidation. 🧪
  • Expect iteration : My first few pipeline runs weren’t perfect. That’s normal. 🔄

Pro tip if you’re starting out :
Don’t overcomplicate your first pipeline. Start with just one automated task — even if it’s just syncing files to S3. Once you see it work, adding more steps becomes almost addictive. ✨

What I’d Improve Next 🛠️➡️

Now that basic CI/CD is in place, I’m thinking about what’s next:

  • Add a notification (via Slack or email) when a deployment succeeds or fails. 🔔
  • Introduce a basic build/test stage before deployment to catch issues earlier. 🧪
  • Explore using GitHub Environments for even safer staging and production pipelines. 🛡️

Automation isn’t a one-time task — it’s a journey. 🛤️
And every small improvement compounds over time.

Final Thoughts 💬

If you’re still stuck in the cycle of manual deployments and wondering if automation is worth the effort — trust me, it is.

The time and peace of mind you gain are priceless.
GitHub Actions turned deployment from a tedious task into something almost… magical. ✨

Have you automated your deployments yet? 🚀
I’d love to hear your stories, struggles, or favorite CI/CD tricks.
Drop a comment below, highlight your favorite parts, and if you found this post helpful, don’t forget to hit the clap button! 👏

Let’s make deployment painless — together. 🤝