Hugo Advanced Setup

Nov 12, 2018 20:25 · 1508 words · 8 minute read hugo setup tutorial deploy https

Advanced Hugo Blog Configuration

At the end of last tutorial we were left with working blog but very uncomfortable deployment flow. To make it easy I will show to configure automatic deployment using CircleCI.

However at first I want to explain how to handle images in posts. It’s not quite obvious since the default way for some time was weird.

Additionally at the end I explained how to use Let’s Encrypt to implement HTTPS.

Handling Images

Hugo By Default

Default way of serving images is to put them into static folder. When you look at the directories structure, you will notice that this will split images with posts in content/posts.

├── archetypes
│   └──
├── config.toml
├── content
│   └── posts
│       └──
├── data
├── layouts
├── resources
├── static
└── themes

Cleaner Way

Fortunately with Hugo version 0.32 we can create page bundles. This allows to put images and other files with post file in one directory. (It’s kind of a surprise it wasn’t possible from the beginning.)

To do this you have to create folder with name of the post, change post filename to and put it into that folder.

$ mkdir content/posts/first-post
$ mv content/posts/ content/posts/first-post/

It should look like this:

├── archetypes
│   └──
├── config.toml
├── content
│   └── posts
│       └── first-post
│           └──
├── data
├── layouts
├── resources
├── static
└── themes

The urls won’t change, only the directory layout. This creates place to put our images for post. You can create subfolders inside first-post to categorize images or just put them next to

I’ll crete subfolder images and put example.png there.

$ mkdir content/posts/first-post/images
$ cp ~/Desktop/example.png content/posts/first-post/images/example.png
├── archetypes
│   └──
├── config.toml
├── content
│   └── posts
│       └── first-post
│           ├── images
│           │   └── example.png
│           └──
├── data
├── layouts
├── resources
├── static
└── themes

To include that image in post you have to edit and use following syntax:


In brackets you should put text that will be displayed if image can’t be loaded. In parentheses you put relative to image location.

Links With Dates

Another small feature I found useful is to make urls contain year and month of post publication. It’s quite simple to setup. In main configuration file (config.toml) insert following:

    posts = "/:year/:month/:title/"

Setup Github Repository

This step will be required for automatic deployment on CI. It’s also a good way to backup your blog. You will have full history of changes, and if something is broken, you can easily change it back.

You will need a Github. It’s free for public repositories. However you will have to be careful not to put any sensitive data inside there. Right now you just have blog files, but when configuring auto deployment it will be important.

I am using private repository so I could upload my work in progress without publishing it to the world. Right now I create new branch and work on post there. When it’s finished I merge it with master branch and it’s automatically deployed and published.

Create Account

Go to and follow Sign Up process. It’s straight forward.

If you are a student I strongly recommend checking out (Student Developer Pack](

Add Keys

After you login, on right top corner click on your profile picture and select Settings. github_settings_button

Then on the left side choose SSH and GPG keys. github_ssh_keys_settings

And create new ssh key by clicking New SSH key github_new_ssh_key_button

In there upload your public ssh key that you already have. adding_new_ssh_key

After clicking Add SSH key it should be listed. ssh_key_added

Install Git

To use git i suggest installing command line program.

$ brew install git
# apt install git

Add Global Config

After installing git you should add global settings. Use e-mail that you used when creating account at Github.

$ git config --global ""
$ git config --global "Konrad Budaj"

Create New Repository

Setup new repository at Github so you could upload your blog files. On main page at Github, next to repositories listing click New new_repository_button

Choose name and create new_repository_form

After creating new repository Github it’s time to upload blog content.

Upload Content

If you have followed my previous tutorial you should already have initialized git repository in your blog folder.

If you haven’t, in main folder type:

$ git init

Then link local repository to Github

$ git remote add origin

Add all files inside current folder by typing

$ git add -A

Commit your changes with -m (message).

$ git commit -m "Upstream blog files"

And push commit to remote by

$ git push --set-upstream origin master

Now you can open your git repository at Github and see, that all files were uploaded.

Automatic Deployment

We wil setup automatic deploy when branch master gets updated. This way you don’t have to upload files to VPS every time from command line.

To do so we’ll use CircleCI which is very powerful tool. You can setup various actions using it. It has free plan of 1000 minutes execution time per month. It’s way more than it’s needed for deployment of simple blog.

Sign Up

Create an account using Github at circleci_signup

Setup Project

Now you should see dashboard. Choose one the left panel Add Projects. add_projects_button

Find your blog repository and push Set Up Project. setup_project_button

In configuration form choose Linux and Go as a language. setup_project_form

Then click Start building at the bottom. The first build will fail, because there is no configuration file.

Setup CircleCI Environment Variables

Your repository might be public. If it is, you need to be more careful with what you upload there. We want to setup configuration file inside the repository which will connect to VPS and copy new files. To do so you need to setup keys to authenticate during the process of deployment.

Setup ssh keys

Don’t use keys that you have on your computer. Create new pair. Run outside of your blog repository. You don’t want to upload keys into Github.

$ ssh-keygen -m PEM -f circleci

Be sure to force PEM format. I was having troubles adding private key that was generated without that option.

This will create two keys, private circleci and public Upload public key on VPS like described in previous blog post but with additional flag in command to specify key.

$ ssh-copy-id -i droplet

Now upload the private key into CircleCI.

On the dashboard click small gear icon next to project name. edit_project

Go to Permissions -> SSH Permissions ssh_permission_menu

And Add SSH key for blog project. add_ssh_key_button

Enter VPS domain name and private key. enter_ssh_key

After adding the key you should see it listed along with fingerprint that we will use later. ssh_key_listing_fingerprint

Environment Variables

Go to Environment Variables under Build Settings. environment_variables_menu

Add there 3 environment variables: DEPLOY_HOST, DEPLOY_PORT, DEPLOY_USER.
DEPLOY_PORT is ssh port that we setup in previous tutorial

Create CirleCI Config

Create folder .circleci in main directory of blog. Inside it, create file called config.yml.

Copy the following config

version: 2
      - image: cibuilds/hugo:latest
    working_directory: ~/blog
      HUGO_BUILD_DIR: ~/blog/public
      - run: apk update && apk add git
      - checkout
      - run: HUGO_ENV=production hugo -v -d $HUGO_BUILD_DIR
      - add_ssh_keys:
            - "7b:8f:bf:5b:1e:6d:53:78:d2:14:ca:57:90:0d:cd:ee"
      - deploy:
          name: deploy to DigitalOcean
          command: |
            if [ "${CIRCLE_BRANCH}" = "master" ]; then
              rsync -azv -e "ssh -o StrictHostKeyChecking=no -p ${DEPLOY_PORT}" --delete ${HUGO_BUILD_DIR}/ ${DEPLOY_USER}@${DEPLOY_HOST}:~/blog
              echo "Not master branch, dry run only"

This config builds Hugo files on CircleCI and then, if it’s master branch, uploads files on VPS.
Change fingerprints to yours, that you got after adding private ssh key to project.

You can now rerun previously failed build. Now it should successfully deploy to your server!

Example flow

When I start writing new post I would create new branch from master.

$ git checkout -b post/my_new_post

Add content, create commit and push changes.
After few commits, when post is finished, I’d merge post/my_new_post into master branch.
This will automatically fire up CircleCI build and upload new files to server.

Https Support

Since there is no login system or any form that could leak confidential information, there is no direct benefit of implementing HTTPS. However it looks more professional and might have good impact on search rankings.

Using Let’s Encrypt you can get free certificate.

Instal CertBot

The best way to install Let’s Encrypt is to use CertBot.
If you have followed my instructions, you should have Ubuntu and Nginx on your VPS.

To install CertBot run following commands as root:

# apt update
# apt install software-properties-common
# add-apt-repository ppa:certbot/certbot
# apt update
# apt install python-certbot-nginx

After that run CertBot to configure with Nginx

# certbot --nginx

Now you should have working HTTPS on your blog.

I did not include information how to setup Google Analytics and Disqus. I found that it’s very dependent on theme you are using. Maybe sometime I will show to configure Google Analytics or Disqus from the very beginning to the end.

tweet Share