static site generation
Thanks to a set of posts by Redpoint’s Tomasz Tunguz, including the December 2018 post titled Reducing Page Loading Times by 90%, we have gotten going with a static site generator to host this blog.
Much like Tomasz, our choice for a static site generator is Hugo, although Jeykll is likely an equally useful choice. Both enable the rollout of a blazing-fast, highly-customizable blog without all the flotsam and jetsam that comes along with trying to roll with a local copy of Wordpress.
This post lays out the technology pieces and the actual mechanics of getting a site up, and out, on the World Wide Web. There are a fair number of steps. Roughly, we need to:
- set up Hugo
- choose a Hugo theme
- do some theme configuration
- customize the theme (beyond the config file)
- author a post
- configure GitHub
- put everything in source control
- get the static pages to web
Set up Hugo
First things first, Hugo is written in Go. And while I don’t speak Go, it tends to matter much less than you might think.
That being said, to run Hugo locally, we need Hugo installed. And to install
Hugo, we first need to download and install Go. For
this work, we were using a Windows machine, so we fetched the latest Windows
installer. At the time, the latest version for Windows was go1.11.4.windows-amd64.msi
.
Once we had Go installed, we then followed the Hugo
documentation for
installing Hugo on Windows. The biggest gotcha is making sure that the PATH
variable is updated so that you can execute hugo commands on the command line.
Given that I am a bit of a dinosaur, I had only previously set PATH variables via the
command line, but Windows 10 allows you to do this on-screen after a couple of
clicks from the Control Panel. Old habits die hard, and I still found echo %PATH%
to be useful. And as suggested by the internet, echo %path:;=&echo.%
makes everything a bit easier to read.
Then, to complete the set up of Hugo’s default site, we followed the Hugo Quick
Start Guide. While Hugo does
not ship with a default theme, we went step-by-step through the directions. In
particular, we ran the command to set up an example in a folder called
quickstart, specifically: hugo new site quickstart
Then we walked through the rest of the guide. If all goes well, Hugo can be
launched with Ananke, the quickstart’s suggested theme. hugo server
If
you get a confirmation on the command line after running hugo server
, the
next step is to fire up a browser navigate to http://localhost:1313/. If
something like a blog appears, we are successfully underway!
Chose a Hugo theme
One of the nice parts of the Hugo is that the development community has built several hundred themes to work with the core generator. One approach to trying out themes is to download the whole basket and cycle through them by inserting a theme flag when launching hugo
from the command line.
Rather than trying to find the perfect theme, we followed Tomasz and used Minimo. Using Minimo as a theme allowed us to get something functional rather quickly. While there are many themes out there, there’s always a bit of an advantage to choosing one that is in use and appears to be well-supported. All this comes at the cost of uniqueness (sorry TT)!
Do some theme configuration
To implement Minimo, we followed the Minimo Docs and made some choices for our components in the config.toml
. We were able to get quite far with the initial configuration file.
Customize the theme (beyond the config file)
Beyond this – and this has its advantages and disadvantages – we also made some edits at the theme level by working with the code directly in /theme
directory. Direct edits to
theme were done with the understanding that these changes will be wiped out if we update the Mimino theme. From a maintenance standpoint, we would be far better off without this level of customization. If possible, finding a theme that does not require this level of tinker is likely the best approach, but sadly we couldn’t help ourselves.
Author a post
Our theme, Minimo, came packaged with a slew of example posts. Many of them were helpful in letting us know about the type of components and configuration that could be accomplished within the theme. We used them as a guide to create our first post.
Adding a post is quite simple. To do so, we created a Markdown file. We named
the file my-first-post.md
to make it easy to identify and placed it into
the \docs
directory. The post needs some header information to appear on
the site. Without a lot of research, the best I could tell as long as the post
date is in the past and the post is not marked with draft: true
, the
post should show up.
As such, save the file and then rebuild the site with hugo server
command.
If you’ve made a post with a recent date in the past, it should appear. Blogging
unlocked!
Configure GitHub
In order to eventually get things in source control, we needed to spend some time on our GitHub setup. Specifically, we needed to set up our credentials so that GitHub could verify us as a valid user with the ability to push code to the remote repositories.
To add credentials on a new machine, we needed to create an SSH key and add that key to our Github account. We used Git Bash to get authentication configured enabling all pushes and pulls from the command line.
Again, the GitHub help pages – including the documentation on how to make sure your SSH keys are set up – assisted as I got things up-and-running.
Some things I ran into included ‘Error: Permission
Denied’
and ‘push declined due to email privacy
restrictions.'
The permission denied errors were overcome by making valid push requests,
specifically referencing git user: git@github.com
. Email errors were fixed
by swapping our no-reply address: git config --global user.email 449125+alexanderdtaylor@users.noreply.github.com
I also had questions on using passphrases that I postponed for a deeper dive. Once appropriately credentialed, we could proceed with both version control and deployment.
Put everything in source control
Here, we can pause a moment and review why we chose GitHub. In short, we wanted to use GitHub for three things. First, every project needs a version control system for source code, and Git & GitHub is a very common solution to that problem. Additionally, we wanted to keep our repositories private. As of 2019, this is now free on GitHub. Finally, we wanted to a place to host our static site.
GitHub Pages is the first solution we put in place for this last problem (although see our Next Steps section on Render for an even better solution).
GitHub Pages requires a GitHub’s Pro Plan to use GitHub Pages for private repositories. The cost for GitHub Pages is $7 per month.
In setting up version control, the first things we did was create two entirely
separate repositories. The first one was named hugo-blog
. hugo-blog
contains all the generator files, or put another way all the code needed to
produce static output. Importantly, it does not include the output. The second
was named alexanderdtaylor.github.io
. The repository name follows the
GitHub Pages’ naming convention to host a static site. Naturally, it contains
what hugo-blog
does not, a set of static files that will be accessible
from http://alexanderdtaylor.github.io.
To set everything up, we created these two unique repositories from GitHub web
interface, and the ran git clone
to bring a copy of those initially empty
repositories locally.^[This setup is a bit in contrast to the Hugo
Docs which
recommends setting up the public
folder in the generator project as a
submodule that points to the other GitHub repository, specifically the
repository named alexanderdtaylor.github.io
. Using submodules is
likely the right way to do this, but in cobbling this together, a
simple-and-stupid, two-repository solution is probably a reasonable approach.]
Get the static pages to web
By the end of this section, we should have a site on GitHub Pages. All this was done with a user/organization-type page, a GitHub Pages' distinction mentioned in the Hugo Docs.
The steps to getting a new post out of our text editor and on the GitHub Pages' site were as follows:
Create a new
.md
file in thedocs
directory within thehugo-blog
project. With a favorite text editor (perhaps Atom), provide meta information in the top of the Markdown file. To publish immediately pick a date that is equal to or later than the current time.To see the post locally, open a command prompt and change directory into
hugo-blog
.
c:\> cd hugo-blog
- Then launch Hugo server.
c:\hugo-blog> hugo server
By default, the server runs on port 1313. After starting the server, we can load http://localhost:1313 in Firefox, Chrome or another modern browser.
- After you’ve got things looking good, return to the command line and
terminate the server session.^[Use
Ctrl+C
to terminate.] Then runhugo
marking our static repository (which lives outside the current directory structure) as our destination.
c:\hugo-blog> hugo -d ../alexanderdtaylor.github.io
- Change directories into our static site. From here, we can add, commit and push our code to the GitHub repository.
c:\hugo-blog> cd ../alexanderdtaylor.github.io
c:\alexanderdtaylor.github.io> git add .
c:\alexanderdtaylor.github.io> git status
c:\alexanderdtaylor.github.io> git commit -m "Updated-blog-2018-01-19"
c:\alexanderdtaylor.github.io> git push origin master
Then, we can check the GitHub repository online to see that the files have been updated, and then check http://alexanderdtaylor.github.io to see the updated blog. Deployment complete!
A final, clean-up step is to get all the code that generated the static site into source control. To add, commit and push changes to the generator:
c:\alexanderdtaylor.github.io> cd ..\hugo-blog
c:\hugo-blog> git add .
c:\hugo-blog> git status
c:\hugo-blog> git commit -m "Update hugo - 2018-01-19"
c:\hugo-blog> git push origin master
Because of the serial nature of pushes, if the full process is not completed hugo
repository and the alexanderdtaylor.github.io
repository could be out-of-sync. At this
point, it is a known risk. Also, all the commands above would benefit from being
batched in a shell script. The good news is there are at least a couple of
examples of shell scripts for Hugo deployment, so a quick Google search will
help here to go one step beyond.
Whew! If all has gone well, the blog is live and hopefully on GitHub Pages. While that’s likely enough for one blog post, for those wishing to trudge on to Next Steps, there’s some nice stuff in the next steps to really make this setup fly.
Next steps: A custom domain
Beyond the deployment to GitHub Pages, we wanted to do two more things to really get stuff moving: add a custom domian and put everything on a CDN.
The first was to add the custom domain, in particular alexanderdtaylor.com
, the
site you may be reading this on now.
We headed over to Namecheap to complete our purchase. Namecheap is pretty handy place to register domains, and the interface is rather intuitive. The only downside we ran into was the lack of support for ALIAS or ANAME records, which isn’t such a big deal but could be a sticking point for others.
With Namecheap in place, we experimented with hooking up our GitHub Pages repository to a custom domain. The documentation to do this is pretty straightforward. First, we update the setting in the GitHub Pages’ repository to let it know we are using a custom domain. And then, we update Namecheap to point to the appropriate IP addresses.
In retrospect, this is pretty easy, but on the first pass I missed scrolling all the way to the bottom of this page to get the set of IP addresses which needed to be setup over at Namecheap.
With that, we were good to go and had our GitHub Pages' hosted site on our custom domain.
Next steps: Putting everything on a CDN
The second step to get things running smoking fast was to move to content delivery network (CDN).
We reached out the team at Render and the provide us early access to a pretty incredible tool for anyone running a static site.
Not only did Render replace the other proposed solution – a clunky Amazon concoction of Route53, S3 and CloudFront – it also simplifies the process by removing the need for us
to build to alexanderdtaylor.github.io
repository.
In fact, we can get away with our single Hugo repository, hand off the build command to Render, and then watch things fly. All in all, it’s pretty phenomenal and the Render team even reached out in the first 15 minutes when I was getting things up-and-running.
As for the steps, we had to unhook the IP addresses that were pointing to our GitHub pages
repository, unlink the Custom Domain and transfer things over so they pointed to Render.
Following the Render Docs, we needed to set up a deployment from our hugo-blog
directory with the following values:
Environment: Static Site
Build Command: hugo --gc --minify
Publish Directory: public
That was enough to get a copy of the site up on Render.
The final step was to set up the custom domain to point to Render. Once we had the CNAME record in place, the process was complete!
A wrap-up
Admittedly, and I think Tomasz would agree, most folks and even most businesses don’t need all this complexity to run a simple blog. Wordpress is more than sufficient. However, Hugo serves then as an excellent tool for folks who want a bit more control without the overhead and are as comfortable working in a text editor and the command line, as opposed to GUIs, plug-ins and all the rest.
In short, we love it. And we hope you love it too.