This is a continuation post from Hugo Setup and Deployment in Hugo series.
In the previous post, we covered the basics of using Hugo. Not only the basics, we also learnt behind the scenes of SSG, getting your site published with GitHub Pages, and finally, we wrote a deployment script written in bash which is used to automatically update the blog’s contents.
The last script we wrote actually is not an effective way to deploy our site because it will generate lots of new HTML files on our machine. With this way, instead of uploading only the files we modified, all these files will be uploaded to the Internet every time we update our site. If you care about network bandwidth or things like SSD endurance, that’s probably not a good thing. Luckily, we can solve this with GitHub Actions.
GitHub Actions basically does the same thing as our deployment script, but, instead of using our own machine to generate and push the HTML files, we’ll let GitHub VM orDocker instances do it for us.
In this post, we’ll focus more on the deployment side.
Goals
- Automate Site Deployment using GitHub Actions
- Use theme as submodule
Prerequisites
Prerequisites are still the same, they are:
- A GitHub account
- Familiarity with CLI
- Familiarity with Git.
Preparation
Clean Up
Let’s start by reviewing the previous site structure, here’s where we left it:
$ tree -a -L 2 -I "\.git"
.
├── .gitmodules
├── archetypes
│ └── default.md
├── config.yml
├── content
│ └── post
├── data
├── deploy.sh
├── 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 = https://github.com/your-username/my-blog.git
active = true
Clean any cached content from the public folder.
$ git rm --cached ./public -f
Then let’s create another GitHub repository with the format your-username.github.io, because we’re going to use this repo as the remote repository of our site.
$ git remote add origin https://github.com/your-username/your-username.github.io.git
For the config.yaml, change the baseURL value with:
baseURL: "https://your-username.github.io/"
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 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
If you don’t use the same theme, simply replace the url and directory parts to match yours.
Configure Github Actions
In the deployment script, we’ve automated several things: site build, commit, and push our site to GitHub. 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.
Create the Workflow
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 codes under the workflow directory with a name gh-pages.yml (./.github/workflows/gh-pages.yml).
name: github pages
# the event
on:
push:
branches:
- main # Set a branch to deploy
jobs:
deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
with:
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
with:
hugo-version: 'latest'
extended: true
- name: Build
run: hugo --minify --cleanDestinationDir
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
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 the --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/test-gh-actions.md
$ echo 'Test GH Action' >> content/post/test-gh-actions.md
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, we can navigate to the site repository and click on the Actions menu (https://github.com/your-username/your-username.github.io/actions).
Revisit your site to verify.
Off-Topic
My Current Deployment
My deployment process is a little bit different. This is because I’ve separated my blog into two repositories: blog-core and fahmifj.github.io . The blog-core repo is a private repo that contains all of my site resources in raw. The fahmifj.github.io repo is a public repository that contains all the generated html from the blog-core repo.
I added a GitHub Actions workflow in the blog-core repo. It runs automatically when I update the repo, generates static HTML files, and then pushes them to the public repo. This blog-core repo held my secret token, which GitHub Actions uses to authenticate and make changes to the public repo.

Note: these are not the actual commands.
Well, it’s not in detail, but it should give you an idea of how it works. Visit this site if you want to learn more about it.
See you in another post!