Stack Link to heading

Hugo Setup Link to heading

I decideded to use the Hugo framework to build my site. It’s an open-source static site generator that uses markdown and looks great. I found a cool theme and went from there.

Building the blog is fairly simple as outlined in their quick start guide:

First, install hugo: brew install hugo

Create a new site: hugo new site blog then cd blog

Initialize the git repo: git init

Add the theme of your choice: git submodule add themes/<theme_name>

And finally, configure the config.toml file using this template, filling in your personal info and adding your profile picture to static/avatar.jpg.

Hugo is then ready to be tested locally with hugo serve

Open your browser and navigate to http://localhost:1313/ to see your progress!

The next step is to start working on your blog posts. Create a new post with hugo new posts/<name_of_blog_post> and write your blog in markdown below the +++.

As you update your content, you can run hugo server -D to run a self-updating local site.

The final step is to copy the contents of themes/layouts into your main layouts directory.

Server Setup Link to heading

Now that the site is working locally, it’s time to build a server to host it.

It’s possible to host a static site on AWS S3, and there are many blog posts detailing how, but I chose to use an EC2 server as I already one to host other sites.

I am using AWS as my cloud provider and Ansible to automate the creation and provisioning of the server. Why use Ansible vs building it manually you ask?

  • Consistent and safe from human error
  • Modular tasks for easy reuse between projects
  • Use of variables to easily control/update parameters of the server (i.e. varaibles for app versions, hostname, etc…)
  • Identical environments for Production, Staging and Local for testing consistency
  • Easy to scale: Can build 1 or 1,000 servers just as easily
  • Saves time for more productivity and eliminates repetitive tasks. I can build a server with one command and work on other things as it’s created and/or control many servers at once.
  • Documentation is inherent. Human readable code vs undocumented black box
  • Not locked into any one provider. Can be easily modified to work anywhere
  • No worries about backing up servers as they can be easily recreated

I git cloned my Ansible template which includes my tasks to connect to AWS EC2, create the instance, update and upgrade the OS, setup my config files, install and configure Apache/Passenger web server and restart the OS. After setting up the playbook, I ran it to provision the server. Check the Readme on information about how it works and how to set it up.

Ansible Template available on GitHub

After the server is configured, I gave it an Elastic IP then setup a CNAME record under my domain’s hosted zone in AWS Route 53 and pointed it to the EIP.

Deploying Link to heading

With the server up and running, we are now able to deploy the Hugo site.

To create the static site, simply run hugo from the hugo directory. This will create a your static site in the public directory.

Deploying your site is as simple as moving the public folder to your server and have it set as your webserver’s document root. There are many ways to do this but I chose to use scp, a secure transfer protocol over ssh.

I set my document root to /var/www/blog/public in my apache config. This directory is owned by the root user. For security best practices, I have setup my ssh keys on a non-root user which has sudo permissions. However, this means that I can’t deploy directly to my document root via scp.

To work around this, I created blog directory in my user’s home dir and symlinked it to /var/www/blog. This allows me to deploy to my non-root user’s home directory and the symlink will duplicate the files to the document root.

Connect to the server and create the soft symlink:

sudo ln -s ~/blog/public/ /var/www/blog/public

You should then be able to deploy your site with scp:

sudo scp -r public <username>@<hostname_or_ip_address>:/home/<username>/blog
  • Set <username> to the user on the server
  • Set <hostname_or_ip_address>
  • Run this command from the hugo directory or change public to /location/of/dir/public

Aliases Link to heading

To make this process easier, I created some aliases in my ~/.profile

Note: Make sure to chage ~/location/of to your blog’s directory on your local machine


This alias deletes the old public directory, builds a new updated one with the hugo command, then pushes it to the server with scp while providing status updates on the command line during each step with the echo command:

alias blog-push="sudo rm -rf ~/location/of/blog/public && echo 'Removed original public directory' && cd ~/location/of/blog && hugo && echo 'Built new hugo static site' && ssh 'rm -rf ~/blog/public' && echo 'Removed old public directory from server' && scp -r ~/location/of/blog/public && echo 'Successfully deployed site :)'"


This alias opens the blog directory in Atom:

alias blog-edit="atom ~/location/of/blog"


This alias loads the local site with drafts from any location and returns to the previous directory when exiting:

alias blog-test="cd ~/location/of/blog && hugo server -D && cd -"

HTTPS with Lets Encrypt and Certbot Link to heading

To enable secure browsing with https we will use Lets Encrypt and Certbot: a free, automated and open Certificate Authority.

Certbot provides easy guides for your specific distribution here. I will cover my setup with Apache and Ubuntu 18.04.

Install certbot:

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot python-certbot-apache

Run certbot in certonly mode and follow the instructions:

sudo certbot certonly --apache

It should find your apache site in /etc/apache2/sites-available/<hostname>.conf and create a cert for that in /etc/letsencrypt/live/. If you encounter issues regarding the apache config, comment out the problem lines in the apache config (most likely the SSL cert location as they haven’t been created yet).

Then, uncomment the commented lines if neccissary and verify your apache configuration is setup properly. The configuration in my Ansible template uses the variables in the var/vars.yml file to redirect incoming connections to port 80 (HTTP) to port 443 (HTTPS), sets the document root to your blog’s public directory and provides the location of the SSL certs created by certbot.

Enable apache SSL module:

sudo a2enmod ssl

Finally, reload the apache config:

sudo service apache2 reload

Important: Make sure to change your Hugo site’s baseurl = in config.toml to https:// and redeploy your site