Hugo Site
This post will cover the complete process of setting up a free Hugo website utilising a Git provider and Netlify. The following was created on a Ubuntu 18.04 system but should be very similar if not the same on other distributions.
Part 0 - Prerequisites
In order to follow this, you will need a Github and Netlify account.
You will also need to use the following software: Git, Hugo, a text editor, a browser and a terminal.
Part 1 - Site Setup
1. Installing Hugo
On Ubuntu, installing Hugo is just one simple command:
$ sudo apt install hugo
We can verify it has been installed correctly and see the version by now typing:
$ hugo version
Hugo Static Site Generator v0.40.1 linux/amd64 BuildDate: 2018-04-25T17:16:11Z
We can see that version 0.40.1 was installed via the Ubuntu repositories.
2. Running Hugo
Before we run the server we need to tell Hugo to create a site and give it a name:
$ hugo new site TestWebsite
We can now change directory into that folder that was just created and start the server:
$ cd TestWebsite
$ hugo server
| EN
+------------------+----+
Pages | 3
Paginator pages | 0
Non-page files | 0
Static files | 0
Processed images | 0
Aliases | 0
Sitemaps | 1
Cleaned | 0
Total in 20 ms
Watching for changes in /home/deadl0ck/Development/TestWebsite/{content,data,layouts,static}
Watching for config changes in /home/deadl0ck/Development/TestWebsite/config.toml
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
As we can see, the server is started on port 1313, so if we now go ahead to the browser and enter localhost:1313
we should see the website running. However, until a theme is applied in the config file, the page will be completely blank. As such, let’s go ahead and add the theme now.
3. Installing a Theme
First we need to make sure Git is installed:
$ sudo apt install git
There are many options when it comes to themes for Hugo, I advise taking some time and looking for a suitable theme based on your website needs. For example, some options cater for text-driven blogs whereas others may be more suited for image based websites. However, if at any point you wish to change your theme, it is a simple modification. As such, don’t worry if you decide you no longer want the theme you downloaded. To download a theme we will need to utilise the git clone function. All themes should list installation instructions, it is important to read these as some themes vary slightly.
For this demonstration I chose to use the hugo-tranquilpeak theme. All themes must be cloned to the themes folder. You can also add the theme as a submodule, however this will be covered later when we make our remote Git repository.
Firstly, make sure you are in the correct folder and then run the git clone command the theme specifies, in this instance it is the following:
$ cd themes
$ git clone https://github.com/kakawait/hugo-tranquilpeak-theme.git
If you now run the server again you will notice that a blank page is still shown, this is because the theme also needs to be specified within the configuration file. Let’s move on to editing the file.
4. Editing the Configuration File
Many themes show an example configuration on the website which can be copied into our own config.toml
file, however, if they do not include this information it can easily be copied from the example within the theme folder itself. To do this we can use the following command:
$ cat themes/hugo-tranquilpeak-theme/exampleSite/config.toml > config.toml
This will take the config.toml
file from the example site provided by the theme and concatenate the contents into the existing configuration file that was generated earlier when we told Hugo to make a new site. Having existing configurations to simply change is much easier than manually building a configuration from scratch. If we now start the server again, we can see that the theme has been applied and the site renders correctly.
Let’s now open the config.toml
file in a text editor and take a look at what options we can alter.
# Tranquilpeak
# Version : 0.4.3-BETA
# Author : Thibaud Leprêtre
# I STRONGLY recommend you to use a CDN to speed up loading of pages.
# There is many free CDN like Cloudinary or you can also use indirectly
# by using services like Google Photos.
# If you want to store images yourself, please read this guidelines:
# For users, if you only use this theme, put your images directly in `source/assets/images` folder
# But if you want to add local images, you can put your images directly in `source/assets/images` folder
# For developpers, if you modify this theme, put your images in `source/images` folder and
# use grunt task `build` to synchronize assets
baseURL = "https://example.org"
languageCode = "en-us"
defaultContentLanguage = "en-us"
title = "Hugo tranquilpeak theme"
theme = "hugo-tranquilpeak-theme"
disqusShortname = "hugo-tranquilpeak-theme"
# googleAnalytics = "UA-123-45"
paginate = 7
canonifyurls = true
[permalinks]
post = "/:year/:month/:slug/"
[taxonomies]
tag = "tags"
category = "categories"
archive = "archives"
[author]
name = "Firstname Lastname"
bio = "Super bio with markdown support **COOL**"
job = "Your job title"
location = "France"
# Your Gravatar email. Overwrite `author.picture` everywhere in the blog
gravatarEmail = "thibaud.lepretre@gmail.com"
# Your profile picture
# Overwritten by your gravatar image if `author.gravatarEmail` is filled
picture = "https://cdn1.iconfinder.com/data/icons/ninja-things-1/1772/ninja-simple-512.png"
# Your Twitter username without the @. E.g : thibaudlepretre
# twitter = "thibaudlepretre"
# Your google plus profile id. E.g : +ThibaudLepretre or 114625208755123718311
# googlePlus = "+ThibaudLepretre"
# Menu Configuration
[[menu.main]]
weight = 1
identifier = "home"
name = "Home"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-home\"></i>"
url = "/"
[[menu.main]]
weight = 2
identifier = "categories"
name = "Categories"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-bookmark\"></i>"
url = "/categories"
[[menu.main]]
weight = 3
identifier = "tags"
name = "Tags"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-tags\"></i>"
url = "/tags"
[[menu.main]]
weight = 4
identifier = "archives"
name = "Archives"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-archive\"></i>"
url = "/archives"
[[menu.main]]
weight = 5
identifier = "about"
name = "About"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-question\"></i>"
url = "/#about"
[[menu.links]]
weight = 1
identifier = "github"
name = "GitHub"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-github\"></i>"
url = "https://github.com/kakawait"
[[menu.links]]
weight = 2
identifier = "stackoverflow"
name = "Stack Overflow"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-stack-overflow\"></i>"
url = "https://stackoverflow.com/users/636472/kakawait"
[[menu.misc]]
weight = 1
identifier = "rss"
name = "RSS"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-rss\"></i>"
url = "/index.xml"
[params]
# Customize date format use to render blog post date, categories and other
# You must use date format used by Go Time package https://golang.org/pkg/time/
# Months (not work with short month like "jan", "feb", etc) are translated if translation exists on i18n folders
# Default format is: January 2, 2006
# dateFormat = "2 January 2006"
# Global keywords configuration. Following keywords will be add to every pages
# keywords = ["development", "next-gen"]
# Syntax highlighter, possible choice between: "highlight.js" (recommanded) and "prism.js" (experimental)
# You can comment it to disable syntax highlighting
syntaxHighlighter = "highlight.js"
# Hide sidebar on all article page to let article take full width to improve reading, and enjoy wide images and cover images. (true: enable, false: disable)
clearReading = true
# Define categories will create hierarchy between parents: `categories = ["foo", "bar"]` will consider "bar" a sub-category of "foo".
# If false it will flat categories.
hierarchicalCategories = true
description = "Hugo tranquilpeak theme demo"
# Customization
# Define the behavior of the sidebar
# 1: Display extra large sidebar on extra large screen, large sidebar on large screen,
# medium sidebar on medium screen and header bar on small screen and
# extra large sidebar is swiped on extra large screen and large sidebar on all lower screen (default)
# 2: Display large sidebar on large screen, medium sidebar on medium screen and
# header bar on small screen and large sidebar is swiped
# 3: Display medium sidebar on large and medium screen and header bar on small screen and
# medium sidebar is swiped
# 4: Display header bar on all screens, extra large sidebar is swiped on extra large screen and
# large sidebar is swiped on all lower screens
# 5: Display header bar on all screens and large sidebar is swiped on large screen
# 6: Display header bar on all screens and medium sidebar is swiped
sidebarBehavior = 1
# Your blog cover picture. I STRONGLY recommend you to use a CDN to speed up loading of pages.
# There is many free CDN like Cloudinary or you can also use indirectly
# by using services like Google Photos.
# Current image is on AWS S3 and delivered by AWS CloudFront.
# Otherwise put your image in folder `static/_images/` (development) or in `source/assets/images/` if you can't or don't want to build the theme,
# and use relative url : `your-image.png`
coverImage = "cover.jpg"
# Display an image gallery at the end of a post which have photos variables (false: disabled, true: enabled)
imageGallery = true
# Display thumbnail image of each post on index pages (false: disabled, true: enabled)
thumbnailImage = true
# Display thumbnail image at the right of title in index pages (`right`, `left` or `bottom`)
# Set this value to `right` if you have old posts to keep the old style on them
# and define `thumbnailImagePosition` on a post to overwrite this setting
thumbnailImagePosition = "bottom"
# Automatically select the cover image or the first photo from the gallery of a post if there is no thumbnail image as the thumbnail image
# Set this value to `true` if you have old posts that use the cover image or the first photo as the thumbnail image
# and set `autoThumbnailImage` to `false` on a post to overwrite this setting
autoThumbnailImage = true
# Your favicon path, default is "/favicon.png"
# favicon = "/favicon.png"
# Header configuration
# The link at the right of the header is customizable
# You can add a link (as an icon) at the right of the header instead of the author's gravatar image or author's picture.
# By default, author's gravatar or author's picture is displayed.
# url: /#search
# icon: search
# class: st-search-show-outputs
# Custom CSS. Put here your custom CSS files. They are loaded after the theme CSS;
# they have to be referred from static root. Example
# [[params.customCSS]]
# href = "css/mystyle.css"
# Custom JS. Put here your custom JS files. They are loaded after the theme JS;
# they have to be referred from static root. Example
# [[params.customJS]]
# src = "js/myscript.js"
# Display `Next` on left side of the pagination, and `Prev` on right side one.
# If you set this value to `true`, these positions swap.
# swapPaginator = true
# Sharing options
# Comment and uncomment to enable or disable sharing options
# If you wanna add a sharing option, read user documentation :
# Tranquilpeak configuration > Theme configuration > sharing-options
[[params.sharingOptions]]
name = "Facebook"
icon = "fa-facebook-official"
url = "https://www.facebook.com/sharer/sharer.php?u=%s"
[[params.sharingOptions]]
name = "Twitter"
icon = "fa-twitter"
url = "https://twitter.com/intent/tweet?text=%s"
[[params.sharingOptions]]
name = "Google+"
icon = "fa-google-plus"
url = "https://plus.google.com/share?url=%s"
[params.header.rightLink]
class = ""
icon = ""
url = "/#about"
# Customize link of author avatar in sidebar
# [params.sidebar.profile]
# url = "/#about"
# Customize copyright value "© 2017 <CUSTOMIZATION>. All Rights Reserved"
# [params.footer]
# copyright = "<a href=\"https://github.com/kakawait\">kakawait</a>"
As we can see, the configuration options are relatively easy to follow. Let’s begin changing some of the values with our own information. Note: anything with a ‘#’ symbol at the beginning is a comment. You can either delete or simply comment out a line you don’t wish to use.
# Tranquilpeak
# Version : 0.4.3-BETA
# Author : Thibaud Leprêtre
baseURL = ""
languageCode = "en-us"
defaultContentLanguage = "en-us"
title = "My Test Website"
theme = "hugo-tranquilpeak-theme"
disqusShortname = "hugo-tranquilpeak-theme"
# googleAnalytics = "UA-123-45"
paginate = 7
canonifyurls = true
[permalinks]
post = "/:year/:month/:slug/"
[taxonomies]
tag = "tags"
category = "categories"
archive = "archives"
[author]
name = "deadl0ck"
bio = "I'm a code monkey"
job = "Devops"
location = "EU"
picture = "https://cdn1.iconfinder.com/data/icons/ninja-things-1/1772/ninja-simple-512.png"
twitter = "deadl0ckio"
# Menu Configuration
[[menu.main]]
weight = 1
identifier = "home"
name = "Home"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-home\"></i>"
url = "/"
[[menu.main]]
weight = 2
identifier = "categories"
name = "Categories"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-bookmark\"></i>"
url = "/categories"
[[menu.main]]
weight = 3
identifier = "tags"
name = "Tags"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-tags\"></i>"
url = "/tags"
[[menu.main]]
weight = 4
identifier = "archives"
name = "Archives"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-archive\"></i>"
url = "/archives"
[[menu.main]]
weight = 5
identifier = "about"
name = "About"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-question\"></i>"
url = "/#about"
[[menu.links]]
weight = 1
identifier = "github"
name = "GitHub"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-github\"></i>"
url = "https://github.com/deadl0ckio"
[[menu.misc]]
weight = 1
identifier = "rss"
name = "RSS"
pre = "<i class=\"sidebar-button-icon fa fa-lg fa-rss\"></i>"
url = "/index.xml"
[params]
# Syntax highlighter, possible choice between: "highlight.js" (recommanded) and "prism.js" (experimental)
# You can comment it to disable syntax highlighting
syntaxHighlighter = "highlight.js"
# Hide sidebar on all article page to let article take full width to improve reading, and enjoy wide images and cover images. (true: enable, false: disable)
clearReading = true
# Define categories will create hierarchy between parents: `categories = ["foo", "bar"]` will consider "bar" a sub-category of "foo".
# If false it will flat categories.
hierarchicalCategories = true
description = "Hugo tranquilpeak theme demo"
# Customization
# Define the behavior of the sidebar
# 1: Display extra large sidebar on extra large screen, large sidebar on large screen,
# medium sidebar on medium screen and header bar on small screen and
# extra large sidebar is swiped on extra large screen and large sidebar on all lower screen (default)
# 2: Display large sidebar on large screen, medium sidebar on medium screen and
# header bar on small screen and large sidebar is swiped
# 3: Display medium sidebar on large and medium screen and header bar on small screen and
# medium sidebar is swiped
# 4: Display header bar on all screens, extra large sidebar is swiped on extra large screen and
# large sidebar is swiped on all lower screens
# 5: Display header bar on all screens and large sidebar is swiped on large screen
# 6: Display header bar on all screens and medium sidebar is swiped
sidebarBehavior = 1
# Your blog cover picture. I STRONGLY recommend you to use a CDN to speed up loading of pages.
# There is many free CDN like Cloudinary or you can also use indirectly
# by using services like Google Photos.
# Current image is on AWS S3 and delivered by AWS CloudFront.
# Otherwise put your image in folder `static/_images/` (development) or in `source/assets/images/` if you can't or don't want to build the theme,
# and use relative url : `your-image.png`
coverImage = "cover.jpg"
# Display an image gallery at the end of a post which have photos variables (false: disabled, true: enabled)
imageGallery = true
# Display thumbnail image of each post on index pages (false: disabled, true: enabled)
thumbnailImage = true
# Display thumbnail image at the right of title in index pages (`right`, `left` or `bottom`)
# Set this value to `right` if you have old posts to keep the old style on them
# and define `thumbnailImagePosition` on a post to overwrite this setting
thumbnailImagePosition = "bottom"
# Automatically select the cover image or the first photo from the gallery of a post if there is no thumbnail image as the thumbnail image
# Set this value to `true` if you have old posts that use the cover image or the first photo as the thumbnail image
# and set `autoThumbnailImage` to `false` on a post to overwrite this setting
autoThumbnailImage = true
# Your favicon path, default is "/favicon.png"
# favicon = "/favicon.png"
# Sharing options
# Comment and uncomment to enable or disable sharing options
# If you wanna add a sharing option, read user documentation :
# Tranquilpeak configuration > Theme configuration > sharing-options
[[params.sharingOptions]]
name = "Facebook"
icon = "fa-facebook-official"
url = "https://www.facebook.com/sharer/sharer.php?u=%s"
[[params.sharingOptions]]
name = "Twitter"
icon = "fa-twitter"
url = "https://twitter.com/intent/tweet?text=%s"
[[params.sharingOptions]]
name = "Google+"
icon = "fa-google-plus"
url = "https://plus.google.com/share?url=%s"
[params.header.rightLink]
class = ""
icon = ""
url = "/#about"
# Customize link of author avatar in sidebar
# [params.sidebar.profile]
# url = "/#about"
# Customize copyright value "© 2017 <CUSTOMIZATION>. All Rights Reserved"
# [params.footer]
# copyright = "<a href=\"https://github.com/kakawait\">kakawait</a>"
Here we have changed the baseurl, author details, title and social accounts parameters to reflect our own. If you don’t intend on using many parts of the configuration, it may be worth deleting those parts to make the file easier to read. For this example I merely edited a few parameters and removed many unwanted parts left by the theme developer. We should now see that the website has rendered with the new information. Note that the baseURL value was left empty.
NOTE: If you intend on adding images to your config (site background images/profile pictures) or posts, I highly recommend using a CDN.
5. Create a Post
Now that we are happy with the theme and have changed the values in the configuration files to reflect our own site, it is time to create the first post. To do this we make sure we are in the websites folder and run:
$ hugo new post/first-post.md
This will create a markdown file titled “First Post”, all posts are stored within the content
folder. We can then open this file in a text editor and begin to produce some content. When you open the file, you will notice that there are some parameters already at the top, these are known as front matter and are used to tag your content with metadata.
---
title: "First Post"
date: 2018-08-30T12:24:41+01:00
draft: true
---
The front matter can be changed globally by editing the archetypes/default.md
file. This can be useful if you know you want to add tags and categories to all posts for example. Also note that by default, the post we created is marked as ‘draft: true’. This means that the post will not show on our website until the value is changed to false. This is a nice feature as it allows us to produce content under Git source control but keep it hidden until it is finished.
If you are unsure of how to write markdown documents I recommend taking a look at one of the many cheatsheets you can find online in order to learn the syntax. This will also make you aware of how to use links, images, embed videos etc.
Part 2 - Git Repositories
6. Check the Configuration
Before we add anything to Git repositories, we will have a final check over the config.toml
file and make sure we have added everything we want for the site. If everything looks good, let’s go ahead and create the new repository on Github.
7. Create New Repository on Github
Once logged into Github, click to add a new repository in the top right of the screen. From here it will ask us to provide some information for the new repository. Let’s fill that in now.
As can be seen in the image above, I have chosen to call the repository TestWebsite, however you can call it whatever you wish. Also note that I did not opt to add a README or licence, these can be added later on and since we are intending on pushing from our local Git repository, it is easier to not add them beforehand.
8. Initialise Local Git Repository
In order to begin using Git we need to initialise the website folder. Firstly, make sure you are in the website folder and then enter:
$ git init
9. Create README and .gitmodules Files
We will do the polite thing and add a README file with a basic description.
$ echo "Test Website" > README.md
We will also add a .gitmodules file so that Github can reference from the original theme location on Github. Effectively, this is a symbolic link. By doing this we allow Netlify to track the theme repository itself and as such build using the latest version. Create the file and edit in whichever text editor you wish.
$ nano .gitmodules
[submodule "themes/hugo-tranquilpeak-theme"]
path = themes/hugo-tranquilpeak-theme
url = "https://github.com/kakawait/hugo-tranquilpeak-theme"
10. Add All Files
To enable Git to keep all files under version control we need to add them. To do this we can simply enter the following to add all our files in the website directory:
$ git add .
11. Commit Files
Now we need to commit the files, the comment should be short and to the point each time you commit something:
$ git commit -m "Initial commit."
12. Add Name and Email
We need to give the local repository name and email information. We do this by entering the following:
$ git config --global user.name "deadl0ck"
$ git config --global user.email deadl0ck@deadl0ck.io
13. Add Remote Repository
Now let’s add the remote Github repository that we set up earlier:
$ git remote add origin https://github.com/deadl0ckio/TestWebsite.git
14. Check Remote Repository Works
We can check to make sure it was setup correctly by entering:
$ git remote -v
origin https://github.com/deadl0ckio/TestWebsite (fetch)
origin https://github.com/deadl0ckio/TestWebsite (push)
As we can see the repository was added.
15. Push to Remote Repository
Now let’s push our files to Github:
$ git push origin master
You should be prompted to enter your username and password for Github. To avoid continuously having to enter your credentials, it is possible to set up SSH for use with Github. I recommend reading the documentation if you are unsure how to do this.
Part 3 - Deploy to Netlify
16. Set up Netlify with Github Repository
Once logged into Netlify you will be prompted to choose your Git provider and then log in via that service. This does not give Netlify your credentials for that provider, instead it provides them with an access token to the API. As I’m using Github for this example, I am able to see the repositories I have on that service. Once you choose the repository to deploy (if you have multiple) you will be taken to the deploy options.
17. Choose Deploy Options
Netlify gives an option to choose the Hugo version that it will use to deploy the website with. I recommend to first use the same version that your operating system uses for compatibility reasons. However, I did not have any problems using newer versions. It is worth checking with the Hugo releases to determine if there will be any breaking changes to newer versions and deploy accordingly.
18. Change Subdomain
If you do not intend on using a custom domain, I advise changing the subdomain that Netlify generated to something more readable such as your site name. By default Netlify has given us a long and random subdomain, this isn’t the greatest thing to be passing around to people, so let’s change it to reflect our site.
Great, now we can use mytestwebsite.netlify.com
rather than the default name which wasn’t very memorable.
19. Change URL in Configuration File
NOTE: This step only applies to some themes. If you find your page doesn’t display properly you should carry out this step.
In order for the site to be properly viewable, is it essential to change the URL value in the config.toml
file to the URL that Netlify shows. This was where we previously left the value blank. For example, here we would change the following value:
baseURL = "https://mytestwebsite.netlify.com/"
NOTE: If you do not intend on using your own domain, congratulations, you are now finished! If you do wish to use a custom domain, read further ahead.
20. Create A and CNAME Records at Domain Registrar
Now that the site is live, let’s look at using our own domain. To use a custom domain we will need to create A and CNAME records at the domain registrar that we purchased the domain from. In my instance it will be Gandi. In order to create these records we will first need a Netlify IP address for the A record and the domain that Netlify generated for us. To get the IP address, simply head over to the Netlify Custom Domains Documentation and find where it specifies this. At the time of writing they are using ‘104.198.14.52’, however this could change in the future so you should always check first.
These records can take up to 48 hours to propagate, however usually it is much sooner. In my experience I have typically only had to wait for around 5-10 minutes.
21. Add Custom Domain
Now that we have created the records we can change the domain on Netlify. Enter the domain you wish to use. Netlify advises using the ‘www’ subdomain. Once you enter the domain you wish to use, Netlify will warn that the domain is already owned and ask if you are the owner, select yes.
Once the DNS records have propagated and Netlify no longer warns of issues, we can move on to deploying the certificates.
22. Deploy Let’s Encrypt Certificates
Finally, deploy the Let’s Encrypt certificates. If under this section it still says “Waiting on DNS propagation”, simply allow some more time and come back. The majority of the time, these should be auto-provisioned for you once the site has been deployed. However, if not, it is simply a case of scrolling down to the HTTPS section and following the one click install and then finalising by pressing “Force HTTPS”.