Hail, Hugo!
One of my goals this holiday season was to relaunch my blog. My previous attempt failed because there was too much friction in actually blogging. It was an Iron-backed Rust web app using PostgreSQL and Diesel on a DigitalOcean host behind NGINX. It was my first time using most of these technologies for anything serious. I learnt a lot about them in the course of building that blog but the fifth blog post never saw the light of day. Deployment was too manual and enhancements were a time sync. Ultimately it became a burden.
Enter static site generators. I know I’m late to the party, but at least I now comprehend the benefit ;) Hugo is one such static site generator that seems fairly popular. And I can see why: it is so easy to get something up and running quickly. Here I’ll show you how to go from running that initial scaffolding command to hitting [insert your domain name] and seeing it fully deployed.
One thing of note is exactly how much Hugo balances convention (or opinion) against configuration. Hugo touts configuration as one of its strengths and this was an initial draw for me. I try to avoid heavily magic-laden frameworks like the plague. Sometimes that can go too far, when the desire to build everything from scratch is strong. So in an effort to find balance, I want to start with the absolute bare minimum and build up from there. We should not add anything that doesn’t have an explicit and understood purpose. Thus far, Hugo has accommodated that, so let’s begin.
Building the basics
Hugo already has a great quick start guide that we’ll follow to begin with.
First install Hugo with your package manager of choice.
# OS X buddies
$ brew install hugo
# Arch compatriots
$ sudo pacman --sync hugo
# Gentoo and friends
$ sudo layman --add go-overlay
$ sudo emerge --ask www-apps/hugo
Let’s create a skeleton site and add a theme. You can browse available themes here. I’ll be using the Minimo theme.
$ hugo new site wild-baguette $ cd wild-baguette $ git init $ git
submodule add https://github.com/MunifTanjim/minimo themes/minimo
This is all you need for a basic site without any content (yet). Let’s pause here and check out the folder structure.
$ tree -I minimo # ignore the theme directory
.
├── archetypes
│ └── default.md
├── config.toml
├── content
├── data
├── layouts
├── static
└── themes
Excluding the theme, there’s only two files! We’ll cover archtype=s in a moment when we add some content, but for now let's open up =config.toml
. You will
likely need to add options specific to the theme you chose. Most importantly,
actually set the theme with the theme
key! You can find out relevant options
for the Minimo theme on this commit I made for this site. Minimo has its own
comprehensive configuration example, but the only required key is the
recentPostsLength
. Forgetting to add this key will give you an error upon
starting the server.
Fire up the Hugo server and navigate to the URL displayed in the output. You should see something beautiful. If not, ensure your configuration is correct.
$ hugo server
Adding content and customising
What good are wild baguettes if you can’t find them? Being good citizens, we’ll share our knowledge on their habitats in our first blog post.
$ hugo new posts/where_to_find_them.md
The posts
directory will be nested under contents
in the project root. Fill
this file with whatever pleases you. The important thing is the front
matter. That’s the bit in the ---
block at the top of the file. By default,
Hugo will use the title
for the post’s title (say what?) and the name of the
file for its URL path. It’s important that title
is set.
If you’re wondering what dictates that front matter, that’s the archetype! Hugo allows you to define your own archetypes to streamline adding the content you wish to provide. The default archetype suffices for a simple blog.
Nesting the new post under the posts
directory will also nest it in the URL.
With that in mind, it makes sense to have a posts
page on the site. We need to
create a new file for that.
$ echo '---\ntitle: Posts\n---' > content/posts/_index.md
Each section, or distinct part of our site, can contain a _index.md
file that
represents the section itself rather than its children, like posts do. The
value we give to title
here will be what’s shown on the section page and in
the navigation bar. One more change: add sectionPagesMenu = "main"
to
config.toml
. The value you pass here should be the name of your menu, which
could be different for your chosen theme. For Minimo, it is main
.
Start the server again and see our new navigation menu and post rendered. The
-D
flag here indicates we want to render draft posts in addition to regular
ones.
$ hugo server -D
Let’s personalise the theme a bit. Minimo gives you a very easy way to override
the CSS with the customCSS
key in config.toml
. You can read about a specific
use case here, where the accent colour is changed.
Changing layout content is down to Hugo, so it should be independent of the
theme. Say we want to add an additional line to the footer of our site. Simply
add files to the layout
directory. They must be the same name as the ones your
theme uses. For Minimo, this meant adding a footer partial for the new content,
as well as overriding the existing footer. You can read more about Hugo’s
solution for customising your theme here.
Thus far, the only thing that tripped me up was Minimo requiring that one
recentPostsLength
key to have a value. Hugo itself has been completely
transparent in what it’s doing. If you look at the folder structure of our
project, you’ll see every file we’ve added has a known purpose. If we want to
make adjustments to common configuration options, config.toml
is a good place
to start. If we want to override specific parts of our theme, we just provide
the override in the layouts
directory. The organisation of our site content
will correspond to the folder structure inside the content
directory. So far,
it’s all very intuitive!
Deploying
I promised we’d deploy this so the whole world would know where to hunt for wild baguettes. Hugo has a good guide on Nanobox deployment. I hadn’t heard of Nanobox before so I did some digging. Essentially they provide a managed Docker container for you. They are not a cloud provider and rely on you having an account with a service such as AWS or DigitalOcean. Once you link the accounts, Nanobox will take care of deployments to the hosts of the cloud provider. Nanobox itself is completely free on the basic plan. Given one of the goals of this project was to streamline the process of actually blogging, this sounded like the perfect solution.
I’ll be using DigitalOcean here because I already had an account. Follow the Nanobox guide to generate a DigitalOcean key and give Nanobox read/write access to your account. Then launch the new app.
You’ll then need to install the Nanobox CLI locally. Nanobox needs two files for
us to tell it how to run our Hugo project: boxfile.yml
, which tells Nanobox
what commands to run, and install.sh
, which is called by boxfile.yml
and
actually installs Hugo inside the container. Add these files to your project,
then tell Nanobox to deploy!
$ nanobox remote add <nanobox-app-name> $ nanobox deploy
Visit your Nanobox portal and find a link that takes you to the deployed site! Too easy!
The next logical step is to set up a domain name for your site. I use NameCheap and have been very happy with their service. The prices are competitive and they provide a very intuitive dashboard for adding DNS records.
Next steps
There’s loads of places we could go from here. If you want your site to get even marginal traffic, definitely get a domain name for it. You’ll probably then want to look into adding TLS to your site. Even if you only serve static content, it’s always a good idea to enable it. After that, you can check out the depths of Hugo’s documentation. We’ve barely scratched the surface of what it’s capable of.
This will probably be the path that I take. I’m impressed with how easy the whole experience was, primarily due to the solid documentation and usability of Hugo. Hopefully you’ll see more content on here soon!