I have used Wordpress for years to run CHNS.tech and all my other websites. It was the easiest way to get content up with it’s GUI interface. Late last year, I saw a few of the people I follow start talking about different Static Site Generator tools for building websites. After doing some research, I finally landed on the system I wanted to transition my website onto…Hugo. I spend pretty much all of January this year building it out and transitioning the whole site over. It was a large undertaking but Hugo made it pretty easy to accomplish. I wanted to put together a guide on how to get it up and running.
Hugo is an open source static site generator written in Go. The idea is simple, you write your content in Markdown files and Hugo takes those files and generates a full static HTML website for you. No databases, no PHP, no backend to manage. The result is an incredibly fast website that is easy to host.
The whole workflow once it’s set up is really smooth. You write a post, run a build command, and your site is updated. It pairs really well with a Git repository and a simple deploy script, which is exactly how I have mine set up. This guide will walk you through getting everything installed and configured and finish with serving your site using Nginx in a Docker container.
Prerequisites
I will be setting this up on Ubuntu Server 24.04. Here are my setup guides for the pieces needed before getting started:
You will also want to have Git installed on your server. It is usually included by default on Ubuntu Server but if not, run the following command:
sudo apt install git -y
Installing Hugo
Hugo provides a pre-built binary that makes installation straightforward. We will grab the latest version directly from the GitHub releases page. At the time of writing this, the latest version is 0.142.0. You can check the Hugo Releases page for the current version and update the filename in the commands below to match.
Download the Hugo extended binary package:
wget https://github.com/gohugoio/hugo/releases/download/v0.142.0/hugo_extended_0.142.0_linux-amd64.deb
Install the package using dpkg:
sudo dpkg -i hugo_extended_0.142.0_linux-amd64.deb
Once it finishes installing, verify that Hugo is available and check the version:
hugo version
You should see something like this:
hugo v0.142.0+extended linux/amd64 BuildDate=2025-02-05T09:24:15Z VendorInfo=gohugoio
As long as you see a version number come back, you are good to go.
Creating a New Site
Now that Hugo is installed, we can create the scaffolding for a new site. Navigate to the directory where you want your site files to live and run the following command. I choose to do mine in the home directory for my Docker user (main non root user on the server).
hugo new site {site name}
This will generate the basic folder structure that Hugo needs to operate. Navigate into your new site directory:
cd {site name}
Now initialize a Git repository inside the site folder. This is important for the next step when we add a theme:
git init
Adding a Theme
Hugo requires a theme before it will build a site. There are a ton of free themes available at themes.gohugo.io. For this walkthrough I will use the Ananke theme as it is the one referenced in the Hugo documentation and is a solid starting point.
Add the theme as a Git submodule so it stays linked to the theme’s repository and can be updated easily in the future:
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
Now tell Hugo to use the theme by updating the configuration file. Open hugo.toml in a text editor:
nano hugo.toml
Add the following line to the file:
theme = 'ananke'
While you have the configuration file open, go ahead and update the other default values to match your site:
- baseURL – This should be the full URL of your site including the trailing slash (ex. “https://chns.tech/").
- languageCode – Leave this as “en-us” unless you need something different.
- title – This is the name of your site that will appear in the browser tab and header.
Save and close the file when you are done.
Creating Your First Post
Now lets create some content. Hugo uses Markdown files for all of its content. To create a new post, run the following command replacing the filename with whatever you want to show up as the post URL:
hugo new content/posts/2026/02-22-my-first-post.md
Hugo will create the file under the content/posts/ directory with some front matter already populated at the top. Open the file to edit it:
nano content/posts/2026/02-22-my-first-post.md
You will see something like this at the top of the file:
---
title: "My First Post"
date: 2026-02-22T00:00:00-00:00
draft: true
---
Add your content below the closing --- of the front matter. Write it in standard Markdown format.
One important thing to note is the draft: true field. Hugo will not include draft posts in the final build by default. When you are ready to publish a post, change this to draft: false.
Building the Site
When you are happy with your content and ready to generate the final static files, run the following command from the root of your site directory:
hugo
Hugo will process all of your content and output the finished static files to a public/ directory. This all happens very fast, one of the big reasons I like Hugo. The contents of the public/ folder is what you will copy to your web server.
Serving the Site with Nginx
Now that we have our site built, lets get it served up. Rather than installing Nginx directly on the server, I run it in a dedicated Docker container using the nginx:alpine image. This keeps things clean and isolated.
You will need Docker installed on your server. If you need a guide for that, I have one here:
First, create an nginx.conf file in your site directory. This is the configuration file the container will use:
nano nginx.conf
Paste in the following configuration and update it for your environment:
server {
listen 80;
server_name {your domain or IP};
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Next, create a docker-compose.yml file in your site directory:
nano docker-compose.yml
Paste in the following and update the port and paths to match your setup:
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./public:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
restart: unless-stopped
This mounts your public/ folder as the web root inside the container and maps your nginx.conf to replace the default Nginx configuration. The :ro flag mounts both as read-only since the container does not need to write to them.
Start the container:
docker compose up -d
Verify the container is running:
docker compose ps
Open a web browser and navigate to your server’s IP address or domain name and you should see your Hugo site live.
When you rebuild your site with hugo in the future, the container will automatically serve the updated files since it is pointing directly at your public/ folder — no restart needed.
Hugo has been one of my favorite tools I’ve added to my home lab. The speed of the builds and simplicity of the workflow makes it really easy to maintain. Once you get comfortable with the basics, there is a lot more to dig into like custom shortcodes, taxonomies, and automating your deployments with a webhook and deploy script so all you have to do is push to your Git repository and your site updates automatically.