It’s happened - Wordpress is gone, and Hugo is now powering my blog
What is it
Hugo is a free open-source Static Site Generator (SSG). I’ve used it before for other projects, and decided to use it for my blog. It takes simple markdown files, and generates the raw HTML needed to display it.
Why did you switch
Wordpress, as awesome as it is for a typical user to create content, is always targetted for hackers. It runs on PHP, which allows executing code based on requests from a remote user. This isn’t good.
I want simple. I want to have my website look the same across all posts, and be light. My Wordpress had been transitioned a few times anyways - from a home server, to Digital Ocean, and then to two Digital Ocean droplets (one for MySQL, one for Nginx and PHP). This helped speed it up significantly, but it could still be faster.
Only getting a C grade sucks. It’s taking almost a full second to deliver less than 200 KB of data? That is unacceptable!
Getting a B is much better! This is running on the same server that was running Nginx and PHP for my Wordpress blog! 870 KB in almost a quarter of a second is much better! And yes, I know that it is currently only HTTP, but my server is going to be rebuilt soon to ONLY contain this site. Then, everything will be HTTPS.
Security reasons
First off, let’s make an assumption - if you’re in IT, you probably care about security. Whether it’s encryption, authorization, accountability, accessibility, secrecy, or find it plain FUN to secure your assets. I like security, as long as I understand what it is.
There are numerous reports of PHP being insecure. Unfortunately, it also runs on many millions of web servers around the world - it’s not going away soon. If I can close that door for potential attackers, then I will have less worry going on.
Because of how popular Wordpress is, especially for bugs, I had scripts that would run daily to get updates to themes, plugins, and the Wordpress Engine itself. It’s great for a set-it-and-forget, but what about those
Source Control
It’s no secret that I like automation. One big factor with automation is having proper Source Control, such as GitHub or GitLab or BitBucket, for examples. Using plain ol’ git
, I now have this website in source control to the point I can roll back changes if I don’t like it. Let’s see that with Wordpress without extensive plugins!
Everything I need is in plain text, except for images. This is the perfect candidate for source control. Currently, my entire website can be rebuilt from its source in under 1 second! This is thanks, largely, due to automation.
What is your new workflow now?
- Come up with an idea to write about (no change from the old process)
- Run
hugo new post
. This will create my template file that I can rename, then edit with plain old Markdown on whatever plain-text editor or IDE I want (big change from Wordpress). I do not need to be on a browser to complete this step. - When I’m ready, I test it. I run
hugo server
from my dev computer, and make sure the site still looks great with the new post. (different process, same step for Wordpress) - When I’m happy, I change
draft: true
todraft: false
, and commit to source control (same effect, extra benefits) - When I run
git push
, my webserver gets the post and rebuilds the site.
This is all able to be done via the command line or your favourite plain-text editor. Programs that include Git compatibility are a plus!
What about testing?
From within Visual Studio Code (where I write my posts now), I have a terminal window available. I run hugo server
, then navigate to the address specified - 127.0.0.1:1313. The site has a reload cookie to automatically reload if it detects a change. The server component listens to the directories for a change. As soon as I hit save, I see the results in my browser.
If I’m working on a draft, I can preview the Markdown files in Visual Studio Code (right-click the tab, choose Preview), or tell Hugo to render drafts as well (hugo --buildDrafts server
). Obviously, this would not happen in production, but for my “dev” box, of course!
How did you do it?
I tried searching online for tutorials, but none fitted my needs. Most tutorials brought up information about using Github Pages - but this isn’t what I wanted. I had a domain, and paid for a hosted server - I wanted to use that!
The basic procedure I followed was:
Ensure my Hugo files were in a Git repo on my “dev” computer. Ensure there is a
.gitignore
file at the root of the repo, and it haspublic/
in it to prevent the output from being added to source control (ideally, you’d only want the sources saved).Ensure my “dev” computer could SSH to my web server, preferrably with an SSH key. Obviously, do not commit this key to source control.
Create the folder where the files would reside after Hugo processed them
Ensure Nginx was installed, and pointed to the
public/
folder that Hugo eventually generates. I used/var/www/talk-about-it/public
for my site.Ensure the same version of Hugo is installed on both my Dev computer and my web server. In my case, I installed it from the binary offered on Hugo’s website.
Copy my Hugo site folder to a temporary folder on my web host.
Create a bare git repo (
git init --bare
) pointing to the folder I just copied. I called this folderwebsite.git
to indicate it is a bare Git repo, and placed it in my home folder.Edit the
hooks/post-receive
script in the bare Git repo. I put the following in this script:#!/bin/bash WORKING_DIRECTORY=$HOME/my-website-working GIT_REPO=$HOME/website.git PUBLIC_WWW=/var/www/talk-about-it/public BACKUP_WWW=$HOME/backup_html MY_DOMAIN=test.talk-about-it.ca set -e rm -rf $WORKING_DIRECTORY rsync -aqz $PUBLIC_WWW/ $BACKUP_WWW trap "error 'A problem occured. Reverting to backup.'; rsync -aqz --del $BACKUP_WWW/ $PUBLIC_WWW; rm -rf $WORKING_DIRECTORY" EXIT git clone --recurse-submodules $GIT_REPO $WORKING_DIRECTORY rm -rf $PUBLIC_WWW/* /usr/local/bin/hugo -s $WORKING_DIRECTORY -d $PUBLIC_WWW -b "http://${MY_DOMAIN}" -v rm -rf $WORKING_DIRECTORY trap - EXIT
Save and exit your text editor. Run
chmod +x hooks/post-receive
to allow it to execute.Add the web server as a remote repository from my dev machine. You can do this from your dev machine by editing the
.git/config
file, and adding the following (replacing USERNAME and SERVERNAME with your actual values):
[remote "prod"]
url = USERNAME@SERVERNAME:website.git
fetch = +refs/heads/*:refs/remotes/prod/*
That’s it! You can test it now from your Dev machine by running git push prod master
(Push the master repo to the remote target prod). In your terminal, you should see some output from Hugo. If you see that it created all the pages successfully, then you should be able to browse to it. If not, check for errors.
Troubleshooting
It’s no secret that automation is not perfect until it’s setup. Let’s work out the moving parts and figure out where the errors happen.
Can you SSH to your Prod (web server) from your Dev machine using your SSH key? If not, fix this. Git won’t be able to push to the server unless it has access to push over SSH.
Is the same version of Hugo installed? I ran into issues where files were not rendering at all, and it’s because I used Debian’s repo, which was many versions behind.
Check the Hugo website for the latest version. You can also run
hugo version
on both machines to verify what version you’re running. If they are the same version, then you will see exactly what prod will look like on dev, and vice versa.Are your themes cloned as submodules? This allows you to get the latest versions of themes as well by running a
git pull --recurse-submodules
on the base of the repository. I added it into the script above, as this was another one of my issues.Is the web server running? On Debian, I can run
systemctl status nginx.service
to see how it’s doing. Ideally, it should be a set-and-forget, especially for serving out only static HTML files. This would be a good candidate to run Nagios checks, or similar.Can the
www-data
user access thepublic/
folder that Hugo creates? If not, you may need to alter the script to allow all users access to read files, and read-execute on folders in your webroot.Basic web checking - Does your DNS server point to the correct IP address? Does your web host have a firewall blocking incoming requests on port 80 and/or 443? Does your web host show it’s listening on those ports (check by running
ss -altn
, and check the outputs). Are you using a proxy that is caching the website for you, such as Cloudflare? Have you tried an Incognito or InPrivate Window in your web browser? What aboutcurl
orwget
?
Conclusion
This challenge made me understand a lot more about Git and the actions I can have it take on demand, or automatically. I do not want to AUTOMATICALLY push to production every time I make a change, so it only re-renders on my web server (in under a second) when I push to it directly.
Using Markdown files will make my writing more universal. Plain text files will survive forever, and with Git as my version control, I only need to clone it to a private Github repo to keep it alive.
You can switch out themes pretty easily, and test them in dev before pushing them to prod. Because we’re in source control, I could choose any other point in my Master branch, and revert to that change.
Because we’re using static HTML files now for the output, my server will be able to handle more requests per second than it could beforehand.