Setting up a website with the Hugo framework

What is hugo?

Hugo is a static-site generator written in Golang, and leverages the JAM architechture. (Javascript, APIs, Markdown)

If you are not yet familiar with Jamstack, consider reading this simple explanation from the official website:

If you dont feel like clicking the link, here are some quick facts to get you on board:

Why i decided to switch to Hugo

WordPress is bloated for my personal use

WordPress has a lot of features that are completely unecessary for this simple blog / website.

Users, management dashboards, plugins, and a need for a database are just some of the functionalities that are a total overkill for my small, personal website.

Hugo foregoes all that, and opts instead for a more simple and streamlined approach with a tiny set of key functionalities, that can be easily expanded when or if there is ever a need to do so.

NOTE: this also makes it harder (or impossible) to use for non-technical people, so be warned!

Content input

This one is highly subjective, but i really dislike the Wordpress editor. I feel like i spend more time wrestling with the formatting than i do writing the actual content.

With Hugo i get to write my content in Markdown, which happens to be my favourite tool for creating formatted text - no need to mess around with an editor!

Deployment and version control

Because Hugo deploys static websites and doesn't need a database, the entire website can be stored in git. It also makes the deployments stupidly simple:

$ git pull
$ hugo -d /path/to/webroot/

Install Hugo and initialize a new site

A complete list of all installation methods for every supported OS can be found here.

I'm going to host my Hugo website on a VPS.
Even is you're planning on using some other hosting method, you can still follow this post until the deployment part.

Let's start by installing Hugo.

I'll be using brew:

$ brew install hugo

Verify installation:

hugo version

Create a folder for your Hugo project and initialize a new site called personal-blog:

$ mkdir website && cd website
$ hugo new site personal-blog

Congratulations! Your new Hugo site is created in /path/to/project/personal-blog.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from or
   create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

Visit for quickstart guide and full documentation.

We now have a folder called personal-blog that houses the code for the website.

A comprehensive explanation of the folder structure can be found here.

Install a theme

While Hugo has a robust built-in theming engine, i encourage you to start by picking one of the hundreds of pre-made themes found here.

Installing and swapping between themes in Hugo is very simple: Just move the source files to the themes/ folder located in the project's root directory.

For this guide we will be using a theme called Manis.

// Method 1: Git - Clone theme from source

$ cd themes/
$ git clone

Cloning into 'manis-hugo-theme'...
remote: Enumerating objects: 55, done.
remote: Counting objects: 100% (55/55), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 1064 (delta 18), reused 35 (delta 11), pack-reused 1009
Receiving objects: 100% (1064/1064), 1.43 MiB | 2.83 MiB/s, done.
Resolving deltas: 100% (522/522), done.
// Method 2: Manual download - Download and uzip

$ cd /path/to/file
$ unzip -d /path/to/project/themes/

To activate the theme, we need to add it's name to configuration.toml:

$ echo 'theme = "manis"' >> configuration.toml

That's all we need to do to get up and running with a new theme.

Start local developement server

To see the website in action, let's start a local Hugo development server:

$ hugo server -D

                   | EN  
  Pages            |  7  
  Paginator pages  |  0  
  Non-page files   |  0  
  Static files     |  7  
  Processed images |  0  
  Aliases          |  0  
  Sitemaps         |  1  
  Cleaned          |  0  

Built in 4 ms
Watching for changes in /path/to/project/personal-blog/{archetypes,content,data,layouts,static,themes}
Watching for config changes in /path/to/project/personal-blog/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address
Press Ctrl+C to stop

The -D flag tells Hugo to also render pages marked as drafts. (We'll get back to that)

Open a browser and go head over to localhost:1313 to see your website in action:

Hugo will watch your project folders, and rebuild the site whenever changes are detected.

Add content

Now that we have a functional website running in our localhost, it's time to add some content.

Navigate to your project's root directory, and create a new folder called blog/ inside the content folder:

$ mkdir content/blog
$ hugo new page blog/
/path/to/project/personal-blog/content/posts/ created

This will create a new file markdown file under /content/blog/ called

Let's take a look at the structure of that file:

title: "My First Post"
date: 2019-03-26T08:47:11+01:00
draft: true

The text within the --- lines are called front matter, and contain arbitary meta-data about the post, that can be accessed by in templates.

All you need to know for know though, is that the draft value should be set to false after you are done with the post, as Hugo will not include it otherwise.

Set up navigation

You might notice we don't yet see our newly created post anywhere.

This is because most themes have their own default configurations, that might not line up with your naming scheme.

Fix this by first copying the themes config.toml over to our projects root.

Now find a [menu] block from your configuration file. Here's what it looks like in the sample theme:

                name = "Post"
                url = "/post/"

                name = "About"
                url = "/about"

As you might already have figured out, each file and folder starting from content/ corresponds to their respective URL paths. Here's some examples:            -->  content/  -->  content/blog/ -->  content/guides/hugo/

Modify the configuration file to match your file structure. Assuming you have been using the sample theme, you can see that the navigation expects to have a /posts/ route.

Since we are calling our posts folder blog, we need to change the menu configuration to match that:

		name = "Blog"
		url = "/blog/"

You should now be able to access the /blog/ route, and see your post.

Don't forget to change the draft value to false when you are done writing your posts!
Hugo will only build and serve files with the draft value of false.

Regardless of the hosting option you are going to use, i highly recommend checking your website's source code to some type of version control.
Usage of a version control system is out of scope for this article, but a good beginner's guide on Git can be found here.

Deploy the site

I'm deploying to a VPN hosted in Digitalocean, using Nginx as the webserver:

$ ssh <server address>
$ cd <hugo git folder>
$ git pull
$ hugo build && mv public/ /var/www/


Hugo is a great solution for people who want their website's to have as little overhead and bloat as possible.

No need to update or patch anything, database can't be hacked because it doesn't exists, pages are ridiculously fast to load and scale well - everything is hassle free.

But as with everything else, there is also certain downsides and limitations to Hugo.

If your page is going to have a lot of changing content, requires complex manipulation of the DOM, a need to track states, or some other form of highly dynamic content, then Hugo obviously isn't the tool for the job.

Hugo is also (most likely) too techincal to be managed and used by non-techincal people. So if you want the marketing department to take care of the content input, maybe just stick to wordpress.

(For more information, here's a great article detailing when not to use hugo.)