Prerequisites

  • Basic understanding of Unix commands and networking

Step 1: Buy a domain

Although not a hard requirement to host a website it is a good idea to have a domain name. This step might also be the hardest because, as we all know, naming things is probably the hardest task for any developer (you also need to find one that isn’t already taken or that doesn’t cost an arm and a leg). I ended up getting my domain from GoDaddy and although there’s lots of option out there (e.g. Namecheap, Hostinger) I went with GoDaddy purely because it was the first one that came to mind.

Why do you need a domain?

A domain name gives your website an identity, making it easier for visitors to remember and find. If you find a name relevant to your content, it can also help with SEO.

Step 2: Get a VPS

Just like with getting a domain, there are many many choices of VPS providers out there. I ended up going with Hetzner because they seemed like the cheapest and who can say no to German efficiency and hydro-powered data centers. I went with the CAX11 plan (2 v-cores, 4GB RAM, 40GB storage) because it was the cheapest and you don’t need much power for a simple blog site.

One thing I didn’t like so much is that during the registration process I had to put in my payment details before even selecting a plan. Not that big of a deal, but it would have been nice to be reassured I wasn’t going to be charged until selecting a service.

Why do you need a VPS?

A VPS is a Virtual Private Server that you can use to host your website. It gives you full control over the server and allows you to install any software you need. There are alternatives that are more managed (e.g. Firebase Hosting but as I mentioned in my previous post, I want to have more control.

Once purchased you have your own private server that you can do whatever (within the law and performance constraints) with. The first thing to do is generate an SSH key pair to access the server.

Generate an SSH key pair

Generate a new SSH key pair using the following command:

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

You can then save it to the default location or specify a different one. I saved mine to ~/.ssh/id_rsa_hetzner because I already had an SSH key pair for GitHub. The private key (the one without pub) is for you to keep and the public key (the one with pub) is what you will add to your new VPS.

Upload the public key to Hetzner

The easiest way is through the Hetzner Cloud Console, but first copy the key to your clipboard by running:

pbcopy < ~/.ssh/hetzner_public_key.pub

Then go to the Security tab and click on SSH keys. Then click on Add SSH key and paste the contents of the public key file. You can also give it a name to help identify it later.

Connect to the server

Connect to the server using the following command:

ssh -i <path-to-private-key> root@your_vps_ip

and you should be in!

To make things easier I added the following to my ~/.ssh/config file:

Host hetzner
  HostName <your_vps_ip>
  User root
  IdentityFile ~/.ssh/<private-key>

so now I only need to run ssh hetzner to connect to the server.

Hetzner VPS password

You can also connect to the server using a password. Hetzner are supposed to to email you the password when you create the server but I never received it. You can reset the password by going to the Server tab, clicking into the server in question and going to the Rescue tab. Then click on Reset Root Password and you should receive an email with the new password. The password is needed if you connect to the terminal through Hetzner’s web console.

Step 3: Modify DNS settings

Now that you have a domain and a VPS you need to connect the two. This is done by modifying the DNS settings of the domain to point to the VPS i.e. when someone types in your domain name they are directed to your VPS’s IP address.

This is done by going to the GoDaddy console and clicking on the Domain tab. Then click on Manage DNS and you should see a list of records. You need to add an A record with the name @ and the value as the IP address of your VPS. @ stands for the root domain. You can also add a CNAME record with the name www and the value as @ to redirect www.yourdomain.com to yourdomain.com.

If you want to host your blog on a subdomain (e.g. blog.yourdomain.com) you should instead add an A record with the name blog and the value as the IP address of your VPS.

DNS records can take a little while to propagate so don’t worry if it doesn’t work straight away. You can check the status of the DNS records by using whatsmydns.net and typing in your domain name. For me it only took ~10 minutes for the records to propagate.

Step 4: Set up Hugo (Locally)

I won’t go through the whole process of setting up Hugo because it’s well documented on the Hugo website. Here’s a quick summary for MacOS:

Install Hugo using Homebrew

brew install hugo

Create your Hugo site locally

hugo new site myblog

Navigate into your new site’s directory, add a theme from themes.gohugo.io, and start creating content as per the Hugo documentation.

To preview your site locally during development, run:

hugo server -D

This will start a local server where you can see your blog live at http://localhost:1313. The -D flag is used to include drafts in the preview. The site will also automatically update when you make changes to the files which is suuuuper useful.

Step 5: Set up Nginx (on VPS)

It’s time to set up the web server on your VPS using Nginx.

What is Nginx?

Nginx is a high-performance web server. It handles incoming HTTP requests and delivers the static content of your website to the user’s browser. It’s a popular choice for serving static sites because it’s fast, lightweight, and easy to configure.

First, ensure that Nginx is installed on your VPS:

sudo apt install nginx

You can check that it’s running by executing:

sudo systemctl status nginx

Now, let’s create an Nginx configuration on the VPS to serve the files generated by Hugo (we’ll transfer them onto the VPS later).

Create a new config file on your VPS using nano (or your preferred text editor - Vim cough cough). Note it’s in the sites-available directory, not sites-enabled:

sudo nano /etc/nginx/sites-available/your-domain.com

Add this information:

server {
    listen 80;
    listen [::]:80;

    server_name your-domain.com www.your-domain.com;

    root /var/www/your-domain.com; # Where your Hugo site will be stored
    index index.html;

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

    # Optional: Set up caching for static files
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ {
        expires 1M;
        add_header Cache-Control "public";
    }

    # Redirect non-www to www
    if ($host = 'your-domain.com') {
        return 301 http://www.your-domain.com$request_uri;
    }
}

We’ll then create a symlink to the sites-enabled directory to enable the site:

sudo ln -s /etc/nginx/sites-available/your-domain.com /etc/nginx/sites-enabled/

You could just copy the file to the sites-enabled directory but symlinks are more flexible so if you ever want to disable the site you can just delete the symlink.

Test the configuration file for syntax errors with the following command:

sudo nginx -t

And finally, restart Nginx for the changes to take effect:

sudo systemctl restart nginx

Step 6: Set up HTTPS

Getting the lovely green padlock next to your URL is super easy with Let’s Encrypt and Certbot.

What is Let’s Encrypt?

Let’s Encrypt is a free, automated, and open Certificate Authority. It provides free SSL/TLS certificates to enable HTTPS on your website.

What is Certbot?

Certbot is a tool that automates the process of obtaining and renewing these certificates from Let’s Encrypt. It configures your web server (like Nginx) to use HTTPS, making it easy for anyone to secure their site without the need for manual certificate management.

On your VPS, install Certbot:

sudo apt install certbot python3-certbot-nginx

Run Certbot to generate and configure your SSL certificate:

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Follow the prompts, and Certbot will handle the rest. Once this is done, your site will be accessible over HTTPS.

Step 7: Deploy!

The time has come to deploy your Hugo site to your VPS.

Run hugo locally to generate the static files:

hugo

Copy the public/ folder to the VPS using scp (secure copy):

scp -r public/* hetzner:/var/www/your-domain.com/

Here I’m using hetzner as the username for my VPS. You should replace this with the username for your VPS as defined in ~/.ssh/config.

You’ll also likely need to change the ownership and permissions of the files on the VPS:

sudo chown -R www-data:www-data /var/www/your-domain.com
sudo chmod -R 755 /var/www/your-domain.com

Finally, confirm that you can see your site!

Bonus: Automating the deployment

This isn’t something I’ve done yet but I expect it would be pretty easy to automate the deployment process using a CI/CD pipeline. You could use GitHub Actions to automatically deploy the site whenever you push to the main branch. I’ll update this guide if I ever get around to doing this.

I hope this was helpful (even if it wasn’t, I’m sure it will be to me in the future). Feel free to reach out to me (contact@dougtodd.dev) if you have any questions or suggestions for improvements. Thanks for reading! 🚀