Blog
WebsiteLoginFree Trial
  • 🏠PagerTree Blog
  • 📣Meet the PagerTree CLI: Your New On-Call Sidekick!
  • 📣OpsGenie Shutdown Announced: Why PagerTree Is Your Best Alternative in 2025
  • 💎Getting Started With Ruby on Rails in 2024 - The Complete Development Environment Guide
  • 📣WhatsApp Notifications
  • 🧠Site Reliability Engineer (SRE) Interview Questions
  • 👑What is System Monitoring?
  • 👑Top 5 Best PagerDuty Alternatives in 2024
  • 🔡Understanding Linux File System: A Comprehensive Guide to Common Directories
  • 🔡Ping Command: A Comprehensive Guide to Network Connectivity Tests
  • 📜Fly.io migrate-to-v2 Postgres stuck in read-only mode
  • 💎Multi-Tenant SSO using Devise
  • ✨PromQL Cheat Sheet: A Quick Guide to Prometheus Query Language
  • 🔡PowerShell Cheat Sheet: Essential Commands for Efficient Scripting
  • 📣Critical Alerts for iOS and iPhone
  • 📣PagerTree 4.0 is finally here!
  • 💎Ruby on Rails Polymorphic Select Dropdown
  • 🧠SRE Metrics: Availability
  • 🚨Incident Response Alert Routing
  • 💎Ruby on Rails Development Setup for Beginners
  • ✨Jekyll site to AWS S3 using GitHub Actions
  • 💎Migrate attr_encrypted to Rails 7 Active Record encrypts
  • 💎Ruby on Rails Cheat Sheet
  • 📣PagerTree Forms Integration
  • 📣Public Team Calendars
  • 📣Slack, Mattermost, Microsoft Teams, and Google Chat
  • 📣On-call Schedule Rotations
  • 📣Maintenance Windows
  • ✨Docker Commands Cheat Sheet
  • 🪄Slack Channel Stakeholder Notifications
  • 📣PagerTree Live Call Routing
  • 🧠The Science of On-Call
  • ✨serverless
    • 🧠What is Serverless?
    • 🧠Serverless Scales
    • 🧠Serverless Costs
    • ✨Serverless Tools and Best Practices
  • ✨Prometheus Monitoring Tutorial
Powered by GitBook
On this page
  • What are GitHub Actions?
  • Tutorial
  • What You’ll Need
  • Desired Workflow
  • Add a GitHub Action Workflow
  • Build and Deploy on Push into Main
  • Create necessary AWS resources
  • Configure The GitHub Action Secrets
  • Testing your new GitHub Action
  • Conclusion

Was this helpful?

Jekyll site to AWS S3 using GitHub Actions

GitHub Actions are a great way to automate the build and deploy process for your repos.

PreviousRuby on Rails Development Setup for BeginnersNextMigrate attr_encrypted to Rails 7 Active Record encrypts

Last updated 9 months ago

Was this helpful?

In this tutorial, I will show you how to build and deploy a Jekyll static site to AWS S3 + Cloudfront using GitHub Actions. At PagerTree we use GitHub Actions to automate the building and deploying of our marketing site .

What are GitHub Actions?

These days, if you have to do anything manually more than a couple of times, you should probably be automating it. make it easy to automate software workflows. At PagerTree, we use GitHub Actions to deploy our marketing site in a continuous and reliable way.

Tutorial

For this tutorial, I’ll make the assumption that you are fairly familiar with git and and already have a setup.

What You’ll Need

Below I’ve listed what you’ll need for this tutorial. I’ll assume you are dangerous enough to create the following on your own and won’t cover how to create these, as it’s out of the scope of this post.

  • Jekyll static site

  • GitHub Account and Repo

  • AWS

    • Account

    • S3 Bucket with static hosting enabled

      • Bucket Permissions - Block all public access - Off -

      • Bucket Policy - Public Access Policy -

      • Static Website Hosting - Enabled -

    • Cloudfront distribution -

    • Access to create IAM User and Policy

Desired Workflow

Our desired workflow should look something like the following:

On push to our repo’s main branch or when manually clicked in GitHub:

  1. Build the main branch.

  2. Deploy the generated static site files to AWS S3.

  3. Create an AWS Cloudfront invalidation.

This is pretty minimal, and you can get waaay fancier, but for the purpose of this tutorial it should help us understand how to use GitHub Actions.

Add a GitHub Action Workflow

Your GitHub Actions definitions live in a special directory in your repo (<repo>/.github/workflows/). Inside this directory, you’ll have all your workflow files (yml format).

Build and Deploy on Push into Main

In your <repo>/.github/workflows/ directory, create a new file called build_and_deploy.yml. Copy and paste the following into your newly created GitHub Action workflow:

name: CI / CD

# Controls when the action will run. 
on:
  # Triggers the workflow on push for the main branch
  push:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
  
env:
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  AWS_DEFAULT_REGION: 'us-west-2'

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Ruby
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: "3.0" # Not needed with a .ruby-version file
        bundler-cache: true
    - name: "Build Site"
      run: bundle exec jekyll build
      env:
        JEKYLL_ENV: production
    - name: "Deploy to AWS S3"
      run: aws s3 sync ./_site/ s3://${{ secrets.AWS_S3_BUCKET_NAME }} --acl public-read --delete --cache-control max-age=604800
    - name: "Create AWS Cloudfront Invalidation"
      run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"

This workflow file is responsible for building and deploying the site. We’ve named it “CI / CD”. It’s pretty self explanatory, but I’ll explain the process:

  1. When a push is made into the main branch (or manual button click in GitHub), run this workflow.

    1. Checkout our main branch.

    2. Build the site (with the production environment).

    3. Uploads output files from our _site directory to our S3 bucket.

    4. Creates a Cloudfront invalidation (so we can see our new site immediately).

Pretty straight forward, but we still need to create a few resources in AWS and configure secrets in our GitHub repository.

Create necessary AWS resources

We’ll need to create 2 AWS resources, namely an IAM Policy and User.

  • IAM Policy - will grant restricted access to deploy to our S3 bucket and create an invalidation on our Cloudfront distribution. You’ll attach this policy to the IAM User.

  • IAM User - will be the credentials the GitHub Action uses to run its aws-cli commands.

Below is the AWS IAM Policy you’ll need to create. You must modify it by replacing a couple of the items below (make sure to replace the ‘<’ and ‘>’ too).

  • <your-bucket-name> - Your S3 bucket name (ex: www.acme.com)

  • <your-aws-account-number> - The 12 numeric characters of your AWS account.

  • <your-distribution-id> - The alpha numeric 14 characters of your associated Cloudfront distribution

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Resource": [
                "arn:aws:s3:::<your-bucket-name>",
                "arn:aws:s3:::<your-bucket-name>/*"
            ],
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ]
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "cloudfront:*",
            "Resource": "arn:aws:cloudfront::<your-aws-account-number>:distribution/<your-distribution-id>"
        }
    ]
}
    
  1. Copy the AWS access key ID and Secret access key to somewhere safe, as we will need these in our next step

Configure The GitHub Action Secrets

In order to use the special variables like ${{ secrets.AWS_ACCESS_KEY_ID }} we’ll need to configure them in the GitHub Actions Secrets. To do this:

  1. In GitHub, navigate to Your Repo > Settings > Secrets > Actions

  2. For each secret below, click the New repository secret button, fill out the form, and click Add Secret

    • AWS_ACCESS_KEY_ID - What you copied in the previous step as AWS access key ID.

    • AWS_SECRET_ACCESS_KEY - What you copied in the previous step as Secret access key.

    • AWS_S3_BUCKET_NAME - The bucket name you set previously in your IAM Policy (ex: www.acme.com).

    • AWS_CLOUDFRONT_DISTRIBUTION_ID - The Cloudfront distribution id you set previously in your IAM Policy.

Testing your new GitHub Action

The easiest way to test your new GitHub action, is to:

  1. Make a small change to your Jekyll site

  2. Commit the change, and push to main.

  3. Navigate to your website (https://www.acme.com), do a hard refresh (Ctrl + F5) and then you should see the changes you just made.

What you should see in the GitHub Actions panel, is a workflow that was created, and the output of the commands that were run.

Note: The first time this runs it could take ~5 minutes. The ‘bundle install’ command for our project took a while, but don’t worry, subsequent builds should use the bundle cache.

Conclusion

Workflows will trigger off events (aka specific activities) that happen in GitHub. There’s , but for this tutorial we will focus on the push and workflow_dispatch events.

The job - Use the Ubuntu latest virtual environment (see )

Setup our Ruby environment () - Installs Ruby (with specified Ruby version if you have a .ruby-version file) and runs ‘bundle install’.

In AWS,

and attach the IAM Policy you just created above.

GitHub Actions Repo Settings Secrets
GitHub Action Run Details

That’s it, you’ve now successfully created a GitHub Action to build and deploy your Jekyll static site to S3 and Cloudfront. I hope you found some value in this tutorial, it’s pretty basic, but if your new to GitHub Actions it should provide a valuable launching pad. Make sure to follow me on , and if you haven’t yet, make sure to checkout PagerTree :)

✨
pagertree.com
GitHub Actions
Jekyll
static website hosted on AWS S3 + Cloudfront website
(2:54)
(3:42)
(5:02)
(9:58)
quite a few
all environment options here
see docs
create a new IAM Policy
Create a new IAM User with programmatic access
twitter
GitHub Actions Documentation
Github Action Run Details
Github Actions