This post is a continuation from my previous post about Hugo Setup and Deployment. In this post, we’ll look into GitHub Actions.

First thing first, let’s sum up what we did previously:

  • We created a site and generated the site’s HTML files (under /public folder) using Hugo.
  • We uploaded these HTML files to a GitHub repository called my-blog, and then we hosted the site by enabling GitHub Pages on that repo.
  • Lastly, we wrote a deployment script written in bash which can automatically generate and push the static HTML files to the GitHub repository.

Using that way, we generate the site’s static HTML files and then we push these newly generated files to the repository. So, there will be a bunch of HTML files generated on our machine over time, and we’re going to upload them all each time we wish to update the site. That’s why this is a waste.

So to avoid that, we’ll employ GitHub Actions! Well, it basically does the same thing as we did, but, instead of using our own machine, we’ll let GitHub VM/Docker instances do it for us.

I know that there are a lot of similar tutorials out there, but let me finish what I’ve started!


The only goal of this post is:

  • Automate Site Deployment using GitHub Actions (I’ll try my best to explain about it!)


Prerequisites are still the same, they are:

  • A GitHub account
  • Familiarity with CLI
  • Familiarity with Git.

A Little Clean Up

Let’s start by reviewing the previous site structure, here’s where we (I) left it:

$ tree -a -L 2 -I "\.git"
├── .gitmodules
├── archetypes
│   └──
├── config.yml
├── content
│   └── post
├── data
├── layouts
├── public # <-- All files under this folder are hosted using GitHub Pages,
│   ├── 404.html
│   ├── assets
│   ├── categories
│   ├── en
│   ├── fa
│   ├── fr
│   ├── index.html
│   ├── index.json
│   ├── index.xml
│   ├── page
│   ├── post
│   ├── robots.txt
│   ├── series
│   ├── sitemap.xml
│   └── tags
├── resources
│   └── _gen
├── static
└── themes
    └── PaperMod

20 directories, 10 files

We’ll now ignore the public folder and remove all of its content including the git repository inside it, because this will be handled by GitHub Actions later.

$ rm ./public/* -r f
$ rm ./public/.git -rf

If you happen to use the public folder as a submodule, remove the following lines from .git/config and .gitmodules

[submodule "public"]
        url =
        active = true

Clean any cached content from the public folder.

$ git rm --cached ./public -f

Then go create another GitHub repository with the format because we’re going to use this repo as the remote repository of our blog.

$ git remote add origin

For the config.yaml, changed the baseURL value with:

baseURL: ""

Add the Theme as Submodule

If you happen to not using theme as submodule previously, this time we will.

Run the following command to add the PaperMod theme we cloned previously as a submodule of our site.

$ git submodule add themes/PaperMod

If you don’t use the same theme, simply replace the url and directory parts to match yours.

Add GitHub Actions

On the previous post, we automated several things: site build, commit, and push our site to GitHub using a bash script. We can call the execution of this script as a workflow that runs a single job, it is to deploy our site. Each command executed within the deployment script (build, commit, push) can be referred as a step.

GitHub Actions are basically the advanced version of the deployment script we created. It’s event-driven, which means we can set a specific event to trigger the workflow. In this case, we’ll create an event that will trigger the site deployment each time we push a new commit to the main branch.

A workflow file for GitHub Actions must be written in YAML syntax, and it’s placed under a special directory called .github/workflows/[here].yml, so let’s create the folder first inside our site repository.

$ mkdir -p ./.github/workflows

We’ll save the following code under the workflow directory with a name gh-pages.yml (./.github/workflows/gh-pages.yml).

name: github pages

      - main  # Set a branch to deploy

    runs-on: ubuntu-18.04
      - uses: actions/checkout@v2
          submodules: true  # Fetch Hugo themes (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
          hugo-version: 'latest'
          extended: true
      - name: Build 
        run: hugo --minify --cleanDestinationDir

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public

You can study the code yourself, because I’m not the one who created it 😅. But, to make sure that the public folder always get cleaned first before build, I added --cleanDestinationDir option.

Let’s commit this workflow and push it to GitHub.

$ git add ./.github/workflows/gh-pages.yml
$ git commit -m "Add gh-pages.yml"
$ git push -u origin main

Trigger the Workflow!

We can create a new post, then add some text to it.

$ hugo new post/
$ echo 'Test GH Action' >> content/post/

Finally, commit the change and push it to your repository to trigger the workflow

$ git commit -m "Trigger build"
$ git push -u origin main

To observe the deployment process/workflow, just navigate to your site repository and click on the action menu (

That’s it!

My Current Deployment

A bit off topic, but I’ll show you my current deployment process, not in detail, but it should give you an idea of how it was like.

My deployment process a little bit different, because I’ve separated my blog into two: blog-core, and the full static HTML site (it’s this site). The blog-core is a private repo, and the full static HTML is a public repository. The blog-core repo contains my draft, notes, and other (a lot of them), which is only available for me locally because of .gitignore.

When a new commit is pushed on the blog-core, GitHub Actions will be triggered to generate the static HTML files. The newly generated HTML files are then pushed to the static site repo. I had to include my secret token in the blog-core so that GitHub Actions could use the token to authenticate itself.


Note: these are not the actual commands.

I’ll separate this section in a dedicated post later! nevermind lol

I found the blog post that explains about this

And here’s the Github Actions that I used