This tutorial details how to set up a blog using Hugo, intall the PaperMod theme, and host it all on GitHub Pages (for free!). This is how I set up this blog you’re reading.



1. Install Hugo

If you’re on macOS, and have homebrew installed, in your terminal:

brew install hugo

For other OSes, and detailed install instructions see here

2. Create new site

Navigate to the folder where you will want the project to reside. The following command will create the folder for the project, and initiate it with the base files:

hugo new site my-hugo-blog

3. Install PaperMod theme

Navigate into the folder for the site you just created, and then clone the PaperMod Theme into your project

cd my-hugo-blog
git clone themes/PaperMod --depth=1

now get rid of the default config.toml, and create an empty config.yml. YAML is much nicer to read and work with IMO.

rm config.toml
touch config.yml

Copy-paste the following settings into config.yml (adapted from the PaperMod example site)

languageCode: "en-us"
title: My Blog
paginate: 5
theme: PaperMod

enableRobotsTXT: true
buildDrafts: false
buildFuture: false
buildExpired: false

  disableXML: true
  minifyOutput: true

  env: production # to enable google analytics, opengraph, twitter-cards and schema.
  title: My Blog
  description: "Example website, built on Hugo."
  keywords: [Hugo, Blog]
  author: Joe Bloggs
  # author: ["Me", "You"] # multiple authors
  images: ["<link or path of image for opengraph, twitter-cards>"]
  DateFormat: "January 2, 2006"
  defaultTheme: dark # dark, light, auto
  disableThemeToggle: false

  ShowReadingTime: true
  ShowShareButtons: true
  ShowPostNavLinks: true
  ShowBreadCrumbs: false
  ShowCodeCopyButtons: false
  ShowWordCount: true
  ShowRssButtonInSectionTermList: true
  UseHugoToc: true
  disableSpecial1stPost: false
  disableScrollToTop: false
  comments: false
  hidemeta: false
  hideSummary: false
  showtoc: false
  tocopen: false

    # disableHLJS: true # to disable highlight.js
    # disableFingerprinting: true
    favicon: "<link / abs url>"
    favicon16x16: "<link / abs url>"
    favicon32x32: "<link / abs url>"
    apple_touch_icon: "<link / abs url>"
    safari_pinned_tab: "<link / abs url>"

    text: ""
    icon: /apple-touch-icon.png
    iconHeight: 35

  # profile-mode
    enabled: false # needs to be explicitly set
    title: Example Blog Site
    subtitle: "This is subtitle"
    imageUrl: "<img location>"
    imageWidth: 120
    imageHeight: 120
    imageTitle: my image
      - name: Posts
        url: posts
      - name: Tags
        url: tags

  # home-info mode
    Title: "Hi there \U0001F44B"
    Content: Welcome to my blog

    - name: linkedin
      url: ""
    - name: twitter
      url: ""
    - name: stackoverflow
      url: ""
    - name: github
      url: ""

      SiteVerificationTag: "XYZabc"
      SiteVerificationTag: "XYZabc"
      SiteVerificationTag: "XYZabc"

    hidden: true # hide everywhere but not in structured data
    hiddenInList: true # hide on list pages and home
    hiddenInSingle: true # hide on single page

  #  URL: "<path_to_repo>/content"
  #  Text: "Suggest Changes" # edit text
  #  appendFilePath: true # to append file path to Edit link

  # for search
    isCaseSensitive: false
    shouldSort: true
    location: 0
    distance: 1000
    threshold: 0.4
    minMatchCharLength: 0
    keys: ["title", "permalink", "summary", "content"]
  - name: Home
    identifier: home
    url: /
    weight: 1
  - name: Blog
    identifier: blog
    url: blog/
    weight: 2
  #- identifier: projects
  #  name: Projects
    #url: projects/
  #  weight: 3
  #- name: Services
  #  identifier: hiring
  #  name: Services
  #  url: services/
  #  weight: 4
# Read:
pygmentsUseClasses: true
    noClasses: false
    # anchorLineNos: true
    # codeFences: true
    # guessSyntax: true
    # lineNos: true
    # style: monokai

You’ll definitely want to adjust the settings in config.yml, you can do that now or later.

If you had any troubles, there are detailed instruction for installing the PaperMod theme here.

4. Create a blog post

You can author content in Hugo using Markdown. Navigate to the /content folder, create a new folder for your blog post, and create a new .md file

cd content
mkdir blog
cd blog

Now in your text editor, open and add the following:

title: "First Post"
date: 2023-03-15T22:57:04+05:30
draft: false
category: Blogging
slug: first-post
  First Blog post
  First Blog post

This is metadata that Hugo will use to build your blog. The most important items are title, and slug. The slug will be used to form the URL.

Below the metadata section, you can add some text. For example:

  First Blog post

Hello, world. This is my first blog post!

5. Preview

Now you’re ready to preview! Start up the server from the project root

cd ../..
hugo server

If all goes well, you should be able to see your site at http://localhost:1313/ (or whichever port hugo tells you on the command line that it’s serving from)

You should be able to see your blog post at http://localhost:1313/blog/first-post

One really nice thing about running hugo server is that it does hot-reloading. This means any time you change a file in your project and save, the server updates the state of the current page to the client using websockets.

Now you might take the time to customize the settings in config.yml, and honing that perfect blog post. Once you’re ready to deploy, you can proceed to build the static site.

6. Build

Build your static website with this simple command:


So fast! So easy! This command will build all of your site into the /public folder. It will now contain all of the static assets that make up your built Hugo website. This is all the stuff that we want to put on the web.

7. Deploy

We want people to be able to read this blog, so it needs to go on the web. We’re going to use GitHub’s free GitHub Pages service to do that. You’ll needa github account.

In GitHub, create a new repository. I’ve been doing this using the GUI on the GitHub website, but the CLI (Command Line Interface) looks nice and would be better suited to a tutorial like this.

Yu can follow these instructions up until the part that says

Congratulations! You’ve successfully created your first repository, and initialized it with a README file.

Now that you’ve set up your repo, go back to your terminal and navigate to the freshly built /public folder. You’re going to set this up to be tracked using git.

cd public
git init
git add .
git commit -m "My first commit"
git remote add origin
git pull origin master # this should cause the README.txt and anything else that github just created to merge in
git push origin master

Great! Your code is up on github. Now we just need to set it up to deploy to GitHub Pages!

Next you need to adjust the settings on your GitHub repo to publish your project to GitHub Pages.

Follow these instructions

Sometimes it takes a few minutes to deploy. Your site will be visible at on the github domain, at a URL similar to

However, with GitHub pages, you can use your own domain. This is what I’m doing with my domain. Even better!
Instructions for using a custom domain here

8. Profit 📈

Congratulations, you now have your very own blog! Now you can watch the profits pour in.

9. Relax

You deserve a break. Why not grab a glass of lemonade and bask in the glory?

Ahh.. that was nice.

Next time you want to post a blog, all you have to do is write another .md and place it in the /blog folder, then do the following from the root of your project

hugo # rebuild the static site, including your latest post
cd public
git add .
git commit -m "posting a blog"
git push origin master # deploy!

10. Extra

Before adding this Hugo blog, my webpage was just a simple html single page portfolio site. I wanted to keep that in place as the homepage, and simply tack on the blog to it.

The way I achieved this was to put my old single pager in /layouts/index.html. Hugo simply uses this file as the template for the homepage when you go to

I had to hack a replica of the top menu directly into that template, but it does the trick well enough for now. I plan to make it behave more like a normal Hugo template down the road, by including the menu properly from the Hugo source.