This not only documents the making of this website, but will also help in setting up any Hugo static site with self hosting.

Ok but why these technologies?

Mainly, nginx is used because it’s already setup and ready to go on my VPS. The GitHub actions auto deployment will allow you to have this as a private repo as well. All serving to a public domain over https.

Tech Used

  • Nginx (Serving)
  • CertBot (SSL)
  • Hugo (Static site generation)
  • VPS (for self hosting)

Recommended

  • A VPS
  • A domain

Hugo

Get Hugo installed onto both your dev machine and your server. It is important to note that some distro’s (like Ubuntu/Debian) may have very old versions of Hugo within their repo. In such cases, you can install Hugo manually from the Github.

GitHub Setup + Hugo

If you already have a github repo ready for your website, you can create a hugo site on top of it using:

hugo new site <repo name> --force

--force is used here because the folder is not empty. For example, it’s already a git repo folder.

Theme

At the time of writing, this website uses the hello-friend-ng theme. This can be installed using:

git clone https://github.com/rhazdon/hugo-theme-hello-friend-ng.git themes/hello-friend-ng

Then you need to let git know about the submodule.

git submodule add https://github.com/rhazdon/hugo-theme-hello-friend-ng.git themes/hello-friend-ng

Within your config.toml, you can set theme = "hello-friend-ng" to activate the theme.

My site configs are as follows:

baseurl      = "https://foreveranapple.com"
title        = "Dave's Learnings"
languageCode = "en-us"
theme        = "hello-friend-ng"
paginate     = 10

[params]
  dateform        = "Sep 3, 2022"
  dateformShort   = "Sep 3"
  dateformNum     = "2022-09-03"
  dateformNumTime = "2022-09-03 15:04"

  # Subtitle for home
  homeSubtitle = "Don't make my mistakes."

  # Set disableReadOtherPosts to true in order to hide the links to other posts.
  disableReadOtherPosts = false

  # Enable sharing buttons, if you like
  enableSharingButtons = true

  # Metadata mostly used in document's head
  description = "Dave's learnings."
  keywords = "homepage, blog, tech"
  images = [""]

[taxonomies]
    category = "blog"
    tag      = "tags"
    series   = "series"

[languages]
  [languages.en]
    title = "Dave's Learnings"
    subtitle = "Don't make my mistakes."
    keywords = "blog, tech"
    copyright = '<a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank" rel="noopener">CC BY-NC 4.0</a>'
    readOtherPosts = "Read other posts"

    [languages.en.params.logo]
      logoText = "Dave's Learnings"
      logoHomeLink = "/"

  [[menu.main]]
    identifier = "blog"
    name       = "Blog"
    url        = "/posts"

Finally, you can add a new blog post using:

hugo new posts/my-first-post.md

Make sure you check everything with:

hugo serve -D

Deployment

Now, let’s serve it. First, we setup Nginx to serve static files. We create a file under /etc/nginx/sites-available/ with our site configurations. For example:

sudo touch /etc/nginx/sites-available/blog

Populate it with the following:

server {
  server_name example.com www.example.com;
  root /home/username/blog/public/; #Absolute path to where your hugo site is
  index index.html; # Hugo generates HTML
  error_page 404 /404.html;
}

Move this file onto sites-enabled:

sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/blog

Make sure that your DNS is setup to point to your server. And the website is ready to serve!

SSL/HTTPS

Let’s get SSL setup. The simplest way is to get it up and going is by using certbot. It’s extremely easy to use with nginx and almost fully automated for certificate renewals.

Install certbot (on Debian/Ubuntu):

sudo apt install certbot python3-certbot-nginx

Afterwards, simply run certbot and follow the prompts.

sudo certbot --nginx

Autodeployment with GitHub Actions

Here’s a simple GitHub actions to get deployment on push:

---
name: CI

# Controls when the action will run.
on:
  # Triggers the workflow on push to master (including merged PRs)
  push:
    branches: [main]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or
# in parallel
jobs:
  # This workflow contains a single job called "build"
  update:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of
    # the job
    steps:
    - name: Updating website.
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.USERNAME }}
        key: ${{ secrets.PRIVATE_KEY }}
        port: ${{ secrets.SSH_PORT }}
        script: |
          cd blog
          git stash
          git pull --force origin main
          git submodule update
          hugo -s /full/path/to/your/static/page --cacheDir ~/hugocache

Make sure to replace parts as needed! And make sure that the secrets are populated.

That’s it!

Hope this helps, and happy making a Hugo static site!