Philipp Conen

Engineering meets hobby.

Build and host a website using Hugo and a Raspberry Pi


As a recommendation, a friend named me Hugo as a fast and easy way to set up a static website. Static websites are fast, tiny, and a good choice for building a blog or testing websites. To get more into the theory of websites, save money, and have full control over the content I proceed by setting up an own web server by using a Raspberry Pi.

To set up the page the following steps are required:

  1. Getting a domain
  2. Setting up a webserver
  3. Link the domain with the webserver
  4. Mapping the ports
  5. Build a static website
  6. Publish the website

1. Getting a domain

To create a personal domain you have the choice to use free or commercial registra.

Based on my research I would recommend no-ip or git pages for a free domain. This may be an option if you want to try out to build an own website. Git pages also provides the benefit of hosting your content on Github. Have in mind, that this domain contains uncommon expressions like or something like that. If this is not on your demand you should spend some money and buy a domain in a common format.

For the commercial version, I choose IONOS to buy a mail service with an included domain. At this point, this was cheaper because STRATO takes another 10 € for the registration process. Both offers cost 1 € per month in the first and 2 € per month after the first year.

After finishing the registration process, one can start setting up the webserver.

2. Setting up a webserver

The simple way would be to buy a service (webspace) that will take the hosting for you. Have in mind that this can cost up to 10 € per month. A cheap alternative would be to turn a Raspberry Pi into a webserver. People who are worried about the electricity costs should take a look at the energy which is needed to power the Raspberry Pi.

To have a clean setup on the Raspberry Pi I recommend making a fresh installation of Raspberry Pi OS. Further, to have a fast and minimal OS I chose Raspberry Pi OS Light which only allows terminal-based SSH connection.
As I would like to outsource the explanation about how to set up the Raspberry Pi, one can find detailed information on this topic here.

Now set up Nginx. As an orientation, I could recommend the following tutorial.

To install Nginx use

sudo apt-get install nginx

and check if it’s active with

sudo systemctl status nginx

If Nginx is not running start

sudo systemctl start nginx

or restart it

sudo systemctl restart nginx.service

If you want to make Nginx start automatically after reboot you can use

sudo systemctl enable nginx.service

To personalize Nginx create a new folder for your website in the Nginx environment

sudo mkdir /var/www/

Go on and create a new config file

sudo nano /etc/nginx/conf.d/

, fill the document with the following content

server {\
        listen 80 default_server;\
        #return 301 https://$server_name$request_uri;\
        root /var/www/;\
        index index.php index.htm index.html;\

and restart Nginx.

At least enable SSL, so your website support https. For this, I choose certbot from the tutorial I mentioned above. Simply use

sudo certbot certonly --webroot -w /var/www/ -d -d

After this step, the https settings can be added by appending the config file of your domain. Open up via

sudo nano /etc/nginx/conf.d/

and append the following content

server {
        listen 443 ssl default_server;

        # SSL

        ssl on;
        ssl_certificate /etc/letsencrypt/live/;
        ssl_certificate_key /etc/letsencrypt/live/;
        ssl_session_cache builtin:1000 shared:SSL:10m;
        ssl_ciphers  HIGH:!aNULL:!MD5:!RC4;
        # HSTS

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

        # global

        root /var/www/;
        index index.php index.htm index.html;

        location / {
                try_files $uri $uri/ =404;

Another restart of Nginx is needed now.

Because the service of certbot is limited to some days it is useful to automize the renewing process.

First, disable the cron service in

sudo nano /etc/cron.d/certbot

by setting an extra # in the following line

#0 */12 * * * root test -x /usr/bin/certbot && perl -e 'sleep int(rand(3600))' && certbot -q renew

Now add a procedure

sudo nano /etc/systemd/system/certbot.service

with the content

Description=Let's Encrypt renewal

ExecStart=/usr/bin/certbot renew --quiet --agree-tos
ExecStartPost=/bin/systemctl reload nginx.service

and a timer

sudo nano /etc/systemd/system/certbot.timer

with the content

Description=Daily renewal of Let's Encrypt's certificates



Finally, start

sudo systemctl start certbot.timer

and enable

sudo systemctl enable certbot.timer

the timer.

In the next step, the webserver needs a connection to the domain. Another keyword on this topic is dynamic DNS (DynDNS).

As a first option, I tried to use Cloudflare. While setting up the connection to IONOS I notice an incompatibility. Cloudfare needs DNSSEC which also needs to be activated in IONOS. But without some extra access, meaning further commercials plugins, this is not possible. \ Contributing to this fact, I found an in-house option of IONOS. Sadly also this option helped me out.
Lastly, I checked the features of my router (Vodafone - EasyBox 804). Vodafone offers the options ([], ([], and ([]. After trying out each option, only ([] worked out for me, which is why I will focus on this option further.

Setting up for dynamic DNS requires the following steps

  1. Sign up
  2. Create your domain
  3. Add your domain
  4. Change the nameserver
  5. Manage your domain
  6. Activate DynDNS on the EasyBox

3.1. Sign up for

Begin by signing up to here.

3.2. Create your domain

If not already happened, one needs to create a domain.

Check out my corresponding tutorial for further information on this.

3.3. Add your domain

Once a domain is available, one needs to add it to

First, click on Domains.

Now enter your domain - in my case, this would be

Select Privat as Share Status.

Below the four nameservers are already listed for the next step.

3.4. Change the nameserver

Now the nameservers of need to be linked to the registrar - in my case IONOS.

To make these changes in the use of IONOS, first, go to Domains & SSL options and chose the corresponding domain. Next chose nameserver in the top flags or hit Change nameserver in the line Destination. Now, one needs to change the four nameservers to


3.5. Manage your domain

On switch to Domains again.

One now should be able to see the activated domain on the bottom. If it is called broken first lean back and wait a few hours for the activating. If this is not fixed until the next day you can check this link for further information. Another option could be to contact the admin via

Beside the domain name click manage.

Now click on each entry which is called Not Yet Configured, enter your current IP-Adress, and save it.

One can check the IP-Adress via this link.

3.6. Activate DynDNS on the EasyBox

Finally, needs to be linked with the DynDNS settings of the router - in my case the EasyBox 804.

Switch to expert mode and look for DNS & DDNS settings under the Internet options.

Next, activate Dynamic DNS and chose as the supplier.

Also enter the domain name, the username, and the password.

If everything went well one should get the feedback: Updated.


IONOS - Change nameservers

4. Mapping the ports

In the last step for the webserver, two ports need to be opened in the router, so visitors outside your network have access to the website. On the one hand, this would be port 80 for http and port 443 for https on the other hand.

Now open up the router settings. In my case (Vodafone - Easy Box 804) I need to visit in my browser and log in. Since port mapping is not standard-setting, one must switch to expert mode. You can find it on the top right (see Figure: Easy Box - Expert mode).


Easy Box - Expert mode

While performing in expert mode switch to Internet-settings. As I already mentioned ports 80 and 443 need to be opened. To do so look for this +-sign and add two maps in the way I did. Have in mind that the IP-address needs to fit with the IP-address of the Raspberry Pi (webserver).


Easy Box - Port mapping

From now on your website should be able to get visitors from outside your network. Remember that for this fact content in Nginx is needed.

5. Using Hugo

If you are new to Hugo I would recommend the quick-start guide. But I will also describe the basic workflow in the following.

5.1. Installation

Working with Hugo starts with the installation. For Ubuntu, one can install Hugo using

sudo apt install hugo

This will also fit for a Raspberry Pi.

5.2. Create a new website project

If you like to create a whole new website, navigate to a path on your working system where you like to save it. Once this is done, one can start building a directory for the website using the following command

hugo new site yourWebsiteName

In the second step, you should choose between building an own theme or use an existing one. You can find many different themes here. Once you find the perfect theme matching your ideas and demands, you should use Git to bring it down to the themes folder of your website project. So if you are already inside your working directory use

git clone /themes

Inside the theme, you can find an exampleSite folder which you can copy to your website project (main folder)

cp /themes/themeNames/exampleSite/* -r .

If you fully agree with the layout of the theme you are lucky and can start editing the config.toml and the files inside the content folder.

5.3. Create new website content

New posts can be created using

hugo new posts/

and new standalone pages can be created using

hugo new

The pre-path posts in the first command is the description of the main menu. It is comparable with a folder where you store files of equal content. As an example, I made an area for my software projects. The config.toml looks like

    name = "Software-Projects"
    url = "software-projects/"
    weight = 40

So to publish new posts for my software projects I need to use the correct URL as a pre-path

hugo new software-projects/

To not only visualize posts (e.g. newPost), one can add some additional text on the sub-directory site by using an index file. This could be seen as an additional introduction for the sub-directory. Have in mind to use the correct syntax for actions like this, e.g.

hugo new software-projects/

Also, it is possible to make a further directory in an existing directory (e.g. sub-sub-directory). So as an example, one could be interested to make several software-related posts for Windows on this one, and Linux on the other hand. For both directories, one can add new posts for the sub-sub-directory, eg.

hugo new software-projects/windows/

and also add additional text in an index file, e.g.

hugo new software-projects/linux/

5.4. Frontmatter

Since every new content file has a frontmatter, one can

  • define a title of the page,
  • manipulate the date and
  • decide whether to publish a site or not (draft).

Have in mind that manipulating the date has a limit. When choosing a date in the future, the site won’t be published before this date.
By default, every new site is created as a draft (draft: true). If a site is finished to be published change the draft key to false (draft: false).

5.5. Attachments

Somehow, one could be interested in attaching files, like a pdf, py or txt. For this demand, Hugo offers the static folder. So as an example, add a test.txt to the static folder of the website project folder. Afterward, call the file and embed it to the website using

[Link to attachment](/test.txt)

Feel free to check out the functionality via this link.

5.6. Markdowns

Finally, the website needs to be filled with content. To highlight and struct the written text, you can check out the markdown synatax.

6. Insert Hugo in Nginx

At this point of time you have a web server that is accessible from outside your network and you have the basic knowledge of how to set up themes and content with Hugo. So the next step would be to get the content of Hugo inside the Nginx webserver.

If you completely develop your website on your web server (e.g. Raspberry Pi), this is really straightforward. Simply copy the public folder of your Hugo website project to the Nginx path (e.g. /var/www/

cp /Path/to/public/* /Path/to/nginx/

If you are working with Hugo on another system as the webserver, you can use scp to send the public folder to the webserver.

To make the ssh and the scp connection easier one could set up a passwordless ssh connect. For this, I’d like to refer to this tutorial.

To now change the default website content of Nginx you need to remove the files inside the /var/www/ folder and replace it with the public folder from the Hugo website project.
To automate this I build three different scripts.

First, I run a preparation script to clean the directories on the webserver. Depending on user rights, one should run the scripts as super user sudo su.

#! /bin/sh
rm -r /var/www/yourWebsite/*
# Remove content inside your webserver
rm -r public/
# Remove former sent public folders

Afterward, I update Hugo on my working system and push the newest version of the public folder to the web server.

#! /bin/sh
cd path/to/website/folder 
# Change to website directory
# Run Hugo - Create new public folder
# Change to home
scp -r path/to/website/folder/public/ pi@
# Copy public folder to webserver (Pi)
ssh pi@
# Webserver login for the next step

Lastly, you can use another script to copy the public folder to the correct path.

#! /bin/sh
cp -r public/* /var/www/yourWebsite/
# Copy the transfered public folder to the webserver
systemctl restart nginx.service
# Restart Nginx


To conclude you should now have a domain (free or commerical) which is linked to your webserver. Also you should have basic knowledge about how to use Hugo and how to fill the webserver with content from Hugo.

In the following, I’d like to offer sources, a small piece of troubleshooting, and some attachments.


Links Latest Access
Certboot - SSL certificate 16.05.2021
Hugo - Nginx - Certboot 16.05.2021
Hugo - Markdown synatax 16.05.2021
Hugo - Themes 16.05.2021
Hugo - Quick start 16.05.2021
IONOS - DynDNS 16.05.2021
IONOS - Buy mail with domain 16.05.2021
IONOS - Connect domain with webserver 16.05.2021
Nginx webserver on Raspberry Pi 16.05.2021
Passwordless SSH connection 16.05.2021
Raspberry Pi OS 16.05.2021


As the website is running, it could be possible that it comes to issues, so that your website is down due to many different aspects. I’d like to share the error sources which encounter me.

No connection

It’s possible, that one is not able to connect to the website. In my case, the problem was that however, the IP-address changed and DynDNS did not update this.
To fix this first check your IP-address. To do so you can log into the router (e.g. EasyBox) or use an online tool.
Now check the IP-address with the one which is saved in the registra record (e.g IONOS).
If they do not match, correct the IP-address inside the records. If this happens often you should check if DynDNS works correctly.

Another possibility for no connection is the fact, that your web server is not reachable.
First, make sure the webserver (e.g.Raspberry Pi) is running, especially Nginx.
Also check if the home network is running, since the connection to the internet may be broken.


Another issue that can occur is, that the website is “forbidden”. While taking some research I found this article , which helped me out.
If one is in a hurry, errors could come up which won’t be recognized. So after you publish new content, make sure that the content arrives at the place it needs to. To make it clear, check if the Nginx webserver is filled with content.


Scripts to publish new content

I wrote three scripts to publish new content. As I am trying to merge all these scripts, I will change this as soon as possible. The workflow for the scripts is:

  1. Prepare the webserver (Pi)
  2. Update Hugo and send content to the webserver (Working system)
  3. Update the webserver (Pi)

Click here to open.

Last updated on 2 May 2021
Published on 2 May 2021
 Edit on GitHub