Hugo On Kubernetes

To learn how to deploy a blogging website using Hugo on Kubernetes

Hugo is one of the most popular  open-source Static Site Generator (SSG) frameworks written in Go.  It allows developers to build fast HTML websites by combining content and templates. Visit the official documentation of Hugo to know more about it.
In this blog, we will see how to deploy a simple HTML website using Hugo on Kubernetes. However, to understand the whole process clearly, we will first set up Hugo’s website on a simple VM and then try to set up in a Docker container. Lastly we deploy the Hugo site on Kubernetes. After setting up, we will also try to make some changes to the website and ensure that the website is updated.

Hugo on VM

In this step, we will see how to deploy Hugo Extended version on a simple VM. The following steps should also work in the desktop environment as well.

Install and extract the tarball

curl -L -o hugo_extended_0.115.4_Linux-64bit.tar.gz && tar -xzf hugo_extended_0.115.4_Linux-64bit.tar.gz

Make the binary executable and move inside /usr/local/bin/

chmod +x hugo && mv hugo /usr/local/bin/hugo

Our installation is done. Confirm by checking the version by running command `hugo version`. 

hugo version

Now we will proceed with setting up a simple website.

Create a new Hugo site

Run the following command to bring a Hugo site with the name “hugosite”. Note that you can give any desired name to your site.

hugo new site hugosite

Initializing the Git repository

cd hugosite && git init 

For the demo purpose, we will be using one of Hugo’s beautiful themes, Anake. There are many ways to set up Hugo’s theme. However, we will use a simple approach of adding a theme as “Git Submodule”. Perform the following steps to add “Anake” theme as Git submodule to our website repository and set the theme to configuration file.

git submodule add themes/ananke && echo theme = \"ananke\" >> hugo.toml

Create your first blog post

It is quite easy to create a blog in Hugo. The `hugo new` command will create a blog post with a default template which we can modify as per our need. Run the following command to get started with your first blog post.

 hugo new posts/

Now, edit the post via `vim` editor or with an IDE provided in this lab which can be opened after clicking on the button “Open IDE” on the right hand side of the page. 

title: "My First Post"
date: 2019-03-26T08:47:11+01:00
draft: true
This is my first experience with Hugo and I am loving it!

Now, start the Hugo Server. Make sure that you are inside the hugosite directory where hugo.toml file is present.

hugo server -D -p 30000 --bind

By default Hugo application binds with the localhost (,  but to access this application from the internet we have to bind the Hugo application to all network interfaces i.e.

Once the server is running, you can access your website through the `app-port-30000` URL on the right side of the page under the `lab-urls` section.

NOTE: Anake is one simple Hugo theme. Hugo has many different themes for the website which you can find in the documentation of Hugo.

Hugo on Docker container

In the previous section, we have seen how to deploy a Hugo based website on a VM, now in this section, we will try to deploy the same website inside a Docker container.

For that, we will need to create a  Dockerfile as below:

# Dockerfile
FROM ubuntu:latest
USER root
RUN apt update -y
RUN apt install wget git   ca-certificates -y
RUN wget && \
    tar -xvzf hugo_extended_0.115.4_Linux-64bit.tar.gz  && \
    chmod +x hugo && \
    mv hugo /usr/local/bin/hugo && \
    rm -rf hugo_extended_0.115.4_Linux-64bit.tar.gz
CMD ["/usr/local/bin/hugo", "-s", "/root/hugosite", "server", "-D", "--bind", "", "--port", "30000"]

Following are few key points from the Dockerfile:

  • Using ubuntu:latest as a base image.
  •  Running a docker container as a ‘root’ user.
  • Updating the package lists and installing required packages. 
  • Installing Hugo from tarball.
  • In the last command , we specify an instruction that is to be executed when a Docker container starts. The command will start the Hugo Server in the container at port 30000, and use the content and templates from the /root/hugosite directory to render a website.

Now, build the Docker image from the Dockerfile and start the container by performing following steps:

docker build -t <DOCKERHUB_USERNAME>/ubuntu:hugo /root/.

The above command will create a docker image from the Dockerfile with a tag. Replace the <DOCKERHUB_USERNAME> with your docker hub username and push the image to the docker registry. 

docker run -dit -v /root/hugosite:/root/hugosite -p 30000:30000 --name hugo <DOCKERHUB_USERNAME>/ubuntu:hugo 

The other command will create a container with a name hugo from the Docker image we have created. It will also mount the host directory(/root/hugosite) to the container directory and map port 30000 of the Hugo server to 30000 of our base VM. We can now use this port of the Base VM along with an IP address to connect to the Hugo Server.  

Now you can access the Hugo application through app-port-30000 URL.

After accessing the website remove the container.

docker stop hugo && docker rm hugo

Setting Up Hugo on K8s

So far, we have seen how to deploy the Hugo website on Base VM and Docker containers. In this section we will deploy the website on Kubernetes.

One of the important questions that arises in the Kubernetes environment is “How can we serve the content and templates to the Hugo server that is running inside the pod?”

The solution to this problem is “Persistent Volume (PV)”.

Pod will use PersistentVolumeClaim to request physical storage. We will specify the hostpath as /root/hugosite ( a directory in the host machine) while creating PV . After installing the Hugo in the base VM, we will keep the contents and templates required for the website inside the /root/hugosite directory . By using PVC, pods can access the host directory. This way, the Hugo server which is running inside the container can access the content as well as templates and will render the web pages.

Create hostPath PersistentVolume

hostPath PersistentVolume uses a file or directory on the Node to emulate network-attached storage.

# pv.yaml
apiVersion: v1
kind: PersistentVolume
  name: task-pv-volume
    type: local
  storageClassName: manual
    storage: 1Gi
    - ReadWriteOnce
    path: "/root/hugosite"

In the above yaml file, we have specified the hostPath as “/root/hugosite” which is the directory with contents and templates on the Base VM that is required by the Hugo Server to render a full HTML website.

Now, apply the above yaml file to create a PV and also verify in the list of persistent volumes.

kubectl apply -f pv.yaml && kubectl get pv

Create a PersistentVolumeClaim

The yaml file for PersistentVolumeClaim looks like below:

# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
  name: task-pv-claim
  storageClassName: manual
    - ReadWriteOnce
      storage: 1Gi

Two key points about above yaml file are:

  • It requests a volume of at 1 gibibytes that can provide read-write access for at least one Node.
  • This PVC is using the PV “task-pv” that we have created in the previous step.

Now, apply the above yaml file to create a PVC and also verify in the list of persistent volume claims.

kubectl apply -f pvc.yaml && kubectl get pvc

Creating a Deployment

The following is a yaml file for the deployment of our website. 

apiVersion: apps/v1
kind: Deployment
  name: hugo-app
    app: hugo-app
  replicas: 1
        app: hugo-app
        app: hugo-app
        - name: task-pv-storage
            claimName: task-pv-claim
        - name: hugo-app
          image: teamcloudyuga/ubuntu:hugo
            - mountPath: "/root/hugosite"
              name: task-pv-storage

Apply the above yaml file to create a deployment and verify it in the list of deployments.

kubectl apply -f deploy.yaml && kubectl get deployment

Now, wait for some time for the deployment to finish. 

Create Service

Once the deployment of the app is ready, create a service yaml file. In this file, we are asking the cluster load balancer that we want your service to expose to the public world so everyone can see the application running. For this use case we take the help of a NodePort. It exposes the service on each Node’s IP. 

# svc.yaml
apiVersion: v1
kind: Service
  name: my-service
    app: hugo-app
  type: NodePort
    - protocol: TCP
      port: 30000
      targetPort: 30000
      nodePort: 30000

Apply the above service yaml file to create a service and verify it in the list of services.

kubectl apply -f svc.yaml && kubectl get svc

Now, access the Hugo application through app-port-30000 URL. You will see the exact same web page as in previous cases while deploying in a base VM or Docker container. 

Updating the Hugo Website

In this section, we will see how to update our website content. Create another markdown file with the name “”.

cd /root/hugosite/content/posts/

Now add the new content to “” file.


Copy and paste the below content:

title: "My Second Post"
date: 2019-03-26T08:47:11+01:00
draft: true

This is my second post in hugo. It's exciting!

After copying the code use Shift + Insert to paste the code in the terminal. Lastly, use Ctrl + D to save the markdown file.

Verify the contents of markdown file as:


Now, if you open app-port-30000 URL, you will see the newer version of Hugo website with two blog posts.


In this blog we have seen how to deploy websites using Hugo on Base VM, on Docker and on Kubernetes. We made changes in websites using markdown files and see the newer version of the website is being displayed.

Join Our Newsletter

Share this article:

Table of Contents