How to create pipeline for hosting a Hugo static site on Netlify, without costs.

Note: commands to execute are only those after the $.

What is Hugo?
Basically is a framework written in Go for building websites. If you want more infos, see https://gohugo.io/.

Why Hugo and not something else (Ghost, Wordpress, …)?
I don’t want to maintain a live server with updates and other things only for writing some static content.
Also, if I want dynamic content like comments, I can integrate Disqus (not for now, and maybe never).
I want the smallest footprint and something that can be portable to other platforms, so pipelining all the static files is the best solution ATM (is anyone saying serverless? 👀).

Prerequisites

  • Possibly a linux distro, like Debian, Ubuntu, Fedora, …

    • if not possible, it’s ok using WSL (Windows 10), OSX shell or a Linux VM
  • Some software: wget, git, text editor, … usual things

  • GitHub and Netlify accounts (free tier)

    For this guide I’m using a debian-based distribution (Debian 10 😏)

Creating a Hugo site

For creating a new site we need to download the software, setup a new project, initialize a inside a local git repo, install a theme and change configuration.

Install Hugo

You can go to Hugo Release page and grab a version for your operating system.

# Download the current latest version of Hugo from Releases page
$ wget https://github.com/gohugoio/hugo/releases/download/v0.56.3/hugo_extended_0.56.3_Linux-64bit.deb

# Install the deb file
$ sudo dpkg -i hugo_extended_0.56.3_Linux-64bit.deb
(Reading database ... 292181 files and directories currently installed.)
Preparing to unpack hugo_extended_0.56.3_Linux-64bit.deb ...
Unpacking hugo (0.56.3) ...
Setting up hugo (0.56.3) ...

# Delete the deb file (if you want, but is useless from now on)
$ rm hugo_extended_0.56.3_Linux-64bit.deb

Create a new project

First, you need to decide where to save your files. In this guide I save the files in the folder ~/git/hugo-site.

# Create a new project
$ hugo new site hugo-site
Congratulations! Your new Hugo site is created in /home/pol/git/hugo-site.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
   create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.

After that you need to know what theme you want to use, you can see all of them (almost all) in the Hugo Themes page. For this guide I’ll install hugo-coder, the same theme that I was using for this site when I started this blog.

Install Theme

You can choose two ways to download and install the theme: download and copy in theme folder or git submodule. I prefer the git submodule way because I can update the theme from the original source without needing to re-download and check every file.

# Enter in the project folder
$ cd hugo-site

# Initialize folder as a git repo
$ git init
Initialized empty Git repository in /home/pol/git/hugo-site/.git/

# Download a theme with git submodule
$ git submodule add https://github.com/luizdepra/hugo-coder.git themes/hugo-coder
Cloning into '/home/pol/git/hugo-site/themes/hugo-coder'...
remote: Enumerating objects: 14, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 1045 (delta 4), reused 8 (delta 4), pack-reused 1031
Receiving objects: 100% (1045/1045), 946.60 KiB | 2.07 MiB/s, done.
Resolving deltas: 100% (478/478), done.

EXTRA: Populating the site with example files

In this guide I’m populating with example files, but this step is not necessary to be able to start using hugo and write posts.

# Populate site with example files from theme
$ cp -r themes/hugo-coder/exampleSite/* .

Edit Configurations

In order to use the downloaded theme, you need to modifiy (or create) the config.toml file. For simplicity you can start using the configuration below. This is the example configuration file with some changes.

baseurl = "/"
title = "johndoe"
theme = "hugo-coder"
languagecode = "en"
defaultcontentlanguage = "en"
paginate = 10
canonifyurls = false
pygmentsstyle = "b2"
pygmentscodefences = true
pygmentscodefencesguesssyntax = true
enableEmoji = true
newContentEditor = "vim"

[params]
    author = "John Doe"
    description = "John Doe's personal website"
    keywords = "blog,developer,personal"
    info = "Full Stack DevOps and Magician"
    avatarurl = "images/avatar.jpg"
    footercontent = "Enter a text here."

    dateformat = "January 2, 2006"

    hideCredits = false
    hideCopyright = false

    rtl = false

    # Use inverted colors
    inverted = false

    # Series see also post count
    maxSeeAlsoItems = 5

    # Custom CSS
    custom_css = []
    
    # Custom JS
    custom_js = []

[taxonomies]
    category = "categories"
    series = "series"
    tag = "tags"

[[params.social]]
    name = "Github"
    icon = "fab fa-github"
    weight = 1
    url = "https://github.com/johndoe/"
[[params.social]]
    name = "Gitlab"
    icon = "fab fa-gitlab"
    weight = 2
    url = "https://gitlab.com/johndoe/"
[[params.social]]
    name = "Twitter"
    icon = "fab fa-twitter"
    weight = 3
    url = "https://twitter.com/johndoe/"
[[params.social]]
    name = "LinkedIn"
    icon = "fab fa-linkedin"
    weight = 4
    url = "https://www.linkedin.com/in/johndoe/"
[[params.social]]
    name = "Medium"
    icon = "fab fa-medium"
    weight = 5
    url = "https://medium.com/@johndoe"


[menu]
    [[menu.main]]
        name = "About"
        weight = 1
        url = "/about/"
    [[menu.main]]
        name = "Blog"
        weight = 2
        url = "/posts/"
    [[menu.main]]
        name = "Projects"
        weight = 3
        url = "/projects/"
    [[menu.main]]
        name = "Contact me"
        weight = 5
        url = "/contact/"

Test your site locally

After editing the config file, you want to see the current status of your site.
With this simple command, you can start a simple local webserver.

# Execute internal local webserver, add -D to show draft posts
$ hugo serve
                   | EN  
+------------------+----+
  Pages            | 51  
  Paginator pages  |  0  
  Non-page files   |  0  
  Static files     |  4  
  Processed images |  0  
  Aliases          | 17  
  Sitemaps         |  1  
  Cleaned          |  0  

Total in 134 ms
Watching for changes in /home/pol/git/hugo-site/{archetypes,content,data,layouts,static,themes}
Watching for config changes in /home/pol/git/hugo-site/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at //localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop

Leave the terminal for a moment (I know it’s difficult 😰) and go to http://localhost:1313/ to see your site.
When you finish looking at your newly created website, press CTRL+C to stop the local webserver.

Hosting on Github

For this guide I’m using GitHub, but is not mandatory, you can also choose GitLab or Bitbucket.

Create a new GitHub repository

Go to https://github.com/new and create a new repository (public or private, it’s the same).

GitHub 1

Execute those commands in the local repository:

git remote add origin [email protected]:pvelati/hugo-site.git 
git add .
git commit -m "first commit"
git push -u origin master

Note: I’m using my private repo, replace with yours.

Configure CI on Netlify

Why on Netlify?

Mainly because of the 100G/month free bandwitdh, the ease of use and the automatic SSL management for the deployments. Also, I can chain this service with other services and build a modern and functional pipeline at zero cost (I will write a post in the next few days about it).

Create netlify.toml file

In order to start the pipeline on netlify it is necessary to create a dedicated configuration file with the parameters for the CI.
This file is netlify.toml

[build]
publish = "public"
command = "hugo --gc --minify"
[context.production.environment]
HUGO_VERSION = "0.56.3"
HUGO_ENV = "production"
HUGO_ENABLEGITINFO = "true"
PHP_VERSION = "7.2"
[context.split1]
command = "hugo --gc --minify --enableGitInfo"
[context.split1.environment]
HUGO_ENV = "production"
[context.deploy-preview]
command = "HUGO_BASEURL=$DEPLOY_PRIME_URL hugo --gc --minify --buildFuture"
[context.deploy-preview.environment]
[context.branch-deploy]
command = "HUGO_BASEURL=$DEPLOY_PRIME_URL hugo --gc --minify"
[context.branch-deploy.environment]
[context.next.environment]
HUGO_ENABLEGITINFO = "true"

There are a lot of parameters, I will explain the meanings in another post. For now let’s consider it valid without worrying.

Push all the files on the git repository

Ok, now you have to push all the local files to the remote git repository. Now you need to execute the commands below.

# Add all local files
$ git add .

# Commit those files with a simple message
$ git commit -m "first commit"

# Push it like there's no tomorrow
$ git push -u origin master

Create a new site on Netlify

Go to https://app.netlify.com/start and click on New site from Git.
Netlify 1

Select GitHub under Continuous Deployment section.
Netlify 2

You need to give Netlify access to your repository (or all your GitHub account, if you are brave 😰).
Netlify 3

After that, select your repo (hugo-site in this example).
Netlify 4

In the Create new site page, delete the build command (the command is automatically taken from the netlify.toml file).
Netlify 5

After that, your site will be automatically enqueued for deployment, with a randon name.
Netlify 6

When it’s ready, the deploy will be PUBLISHED.
Netlify 7

Check these parameters on the Settings page under Continuous Deployment section.
Netlify 8

Check your newly deployed site

Click on green link with the deploy URL to check the site. You should see something like this. Site 1

Change site name

Almost surely the random name is not suitable for your needs. To change it, go to Settings -> Domain management and edit parameters under Custom domains (click on the three dots … ).
Netlify 9

Change the name to another not already used by other users. The system will notify you if there are problems with the name.
Netlify 10

There’s no need to re-deploy your site, the name will be available asap.
Site 2

Test CI with some updates

You saw that the pages on the test site were half in English and half in Portuguese. We need to fix that, and this can be a good example for the CI test.

First, you can remove some files from the content folder.

cd content
rm about.pt-br.md contact.pt-br.md projects.pt-br.md 

After that, you can change something in config.toml. Below you can find a modified version.

baseurl = "/"
title = "johndoe"
theme = "hugo-coder"
languagecode = "en"
defaultcontentlanguage = "en"
paginate = 10
canonifyurls = false
pygmentsstyle = "b2"
pygmentscodefences = true
pygmentscodefencesguesssyntax = true
enableEmoji=true
newContentEditor = "vim"

[params]
    author = "Johnny Doegemberg"
    description = "My personal website"
    keywords = "blog,developer,personal"
    info = "On the Internet, nobody knows you're a dog"
    avatarurl = "images/avatar.jpg"
    footercontent = "Enter a text here."

    dateformat = "January 2, 2006"

    hideCredits = false
    hideCopyright = false

    rtl = false

    # Use inverted colors
    inverted = false

    # Series see also post count
    maxSeeAlsoItems = 5

    # Custom CSS
    custom_css = []
    
    # Custom JS
    custom_js = []

[taxonomies]
    category = "categories"
    series = "series"
    tag = "tags"

[[params.social]]
    name = "Github"
    icon = "fab fa-github"
    weight = 1
    url = "https://github.com/johndoe/"
[[params.social]]
    name = "Gitlab"
    icon = "fab fa-gitlab"
    weight = 2
    url = "https://gitlab.com/johndoe/"
[[params.social]]
    name = "Twitter"
    icon = "fab fa-twitter"
    weight = 3
    url = "https://twitter.com/johndoe/"
[[params.social]]
    name = "LinkedIn"
    icon = "fab fa-linkedin"
    weight = 4
    url = "https://www.linkedin.com/in/johndoe/"
[[params.social]]
    name = "Medium"
    icon = "fab fa-medium"
    weight = 5
    url = "https://medium.com/@johndoe"

[menu]
    [[menu.main]]
        name = "About"
        weight = 1
        url = "/about/"
    [[menu.main]]
        name = "Blog"
        weight = 2
        url = "/posts/"
    [[menu.main]]
        name = "Projects"
        weight = 3
        url = "/projects/"
    [[menu.main]]
        name = "Contact me"
        weight = 5
        url = "/contact/"

Commit files triggering deployment

After editing all the files you want, you need to upload them to the Git repository. So, usual commands.

git add .
git commit -m "change config"
git push

As you can see, on the dashboard there is a new deployed job, with your commit message.
Netlify 11

After deploy’s completion, you can check your site on the previously defined URL.
Site 3

EXTRA: Commit files without triggering deployment

It is not always necessary to execute a new deployment every time a file is changed. So, in order to avoid re-deploy everything, you need to add [skip ci] to your commit message.

git add content
git commit -m "commit without deploy [skip ci]"
git push

Note: you can omit [skip ci] if you are doing lots of commit without pushing. Netlify reads only the last commit message.

Conclusions

Ok, it was not hard. Now you can manage your site easily and without paying a dime.
You can start writing some posts, place them in posts folder and then git add, commit and push 👌.
There are still other features and functionalities that I’m discovering while I’m using this system. I will write more about it later in another posts.