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! 🚀