skip to content
blog.metters.dev
Table of Contents

This article serves as a practical guide for hosting a static website on a cloud service provider. The description particularly focuses on running the site on a virtual private server (vps). The primary goal of this guide is to act as a reminder for my future self, but cheaper or even free alternatives do exist (for example, GitHub Pages).

Overview—the most important steps

  1. Create ssh keys
  2. Set up vps instance (using ssh key)
  3. Create and and configure your server
  4. Configure DNS record

Prerequisites

To follow through with this guide, you need to own a domain. Before buying a domain consider that some domains (e.g. *.dev) require the TLS certificates. Caddy does this out-of-the-box, but it is also possible to use another web server. More on how to automatically update TLS certificates with Let’s Encrypt.

You need an account at Hetzner or another cloud service provider. Any provider will do, as long as you can buy a server instance, e.g. a vps or dedicated server. I will skip a description on how to buy a domain and how to set up an account at Hetzner.

Create ssh keys

To connect with your vps via ssh, we need to create a set of ssh keys. This can be skipped, if you do not want to use ssh keys for authentication (in that case the password for your root-login will be sent to your email-address by Hetzner).

Linode’s documentation describes step-by-step what to do. So I am only going to summarise the commands:

Terminal window
cd ~/.ssh # create directory if it does not exist
ssh-keygen -t ed25519 -C "your_email@example.com"
Lines that require input are highlighted
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/metters/.ssh/id_ed25519): /Users/metters/.ssh/awesome-ssh-key
Enter passphrase for "/Users/metters/.ssh/awesome-ssh-key" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/metters/.ssh/awesome-ssh-key
Your public key has been saved in /Users/metters/.ssh/awesome-ssh-key.pub
The key fingerprint is:
SHA256:FtzRCJuU23HagYN2qmZj6s2lj5/8kBtLv6fiFt5Osjg your_email@example.com
The key's randomart image is:
+--[ED25519 256]--+
| o+.+ |
| o++*.+ |
| .==.* . |
| . =.o . |
| .S |
| *... |
| = oBo. |
| .oEB=@. . |
| .. **X+*+ |
+----[SHA256]-----+

Set up vps instance (using ssh key)


Create an account for cloud service

Click to expand and read about cloud service providers and registration

Create an account at a service provider that offers cloud service. You can pick any service provider you want, however I will focus on Linux (Ubuntu). There are differences with the CPU performance, amount of storage, maximum traffic, etc.

Hetzner

I am currently using Hetzner (scroll to “Prices”)

  • Sign up with referral link (20 Euros cloud credit for you and me)
  • Sign up without referral link

Alternatives

These are some options I came in touch with:

  1. Strato was recommended to me, because they are based in Germany (so is Hetzner), and they seem to be cheaper than Hetzner. For example, they offer three tiers of service named “mini-vserver”, one of which can be tested for free for one month (as of 17.06.2023)
  2. Linode (check the pricing for “Shared CPU”) is located in the United States. I think they are worthy to be mentioned because I found their documentation well written. It helped me a lot to set up my linux instance.
  3. Vultr is an American cloud services provider that is famous for their powerful API that lets users automate their cloud server deployment.
  4. Uberspace is a Germany-based web hosting company that lets the users decide how much they pay for the service, so those who cannot afford the service otherwise can. Others that are more fortunate are kindly asked to pay more to help others out.

Create and configure your server

Create a new project

Set up a new project and a new server using Ubuntu as the operating system. You are going to be asked to add an ssh key. It is possible now to configure firewall rules either with the GUI or using cloud init config. I configure ufw directly on the Ubuntu instance (further below), but of course there’s no reason to not configure the server with the help of those two other options.

The GUI will display the ip address of your server, as soon as it is set up.

Update ssh config

The next step is to set up the ssh config on your local machine. Open the file via:

Create ssh config file
vim ~/.ssh/config

Add the following entry

~/.ssh/config
Host awesome-vps
Hostname 5.75.228.235 # change to your ip address
User root
IdentityFile ~/.ssh/awesome-ssh-key

Recommended: Add a non-sudo user

Add a non-sudo user

Connect via ssh:

Connect to your vps
# with configured ssh config:
ssh awesome-vps
# without configured ssh config:
ssh root@5.75.228.235 # change to your ip address

This connects you as root, but I would rather connect as a less privileged user. The following part illustrates how to add another user. You can use the same ssh key as root, but you should create and use another set of ssh keys instead!

To start the dialogue to add another user, e.g. with the name metters, execute:

Add a new user
adduser metters

You will be prompted to enter a password and additional information for the new user. Grant administrative privileges to metters by adding the account to the sudo group. After that, whenever there are commands that need to be executed as user with root privileges, you can just put sudo in front of the command. Then switch to the new user account (the prompted password is the one you set up for metters) and create a .ssh directory for the new user. Configure the permissions for the new directory and create a file, e.g. named authorized_keys.

Configure the permissions and set up ssh keys for the new user
su - metters
usermod -aG sudo metters
mkdir ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys

Copy and paste the contents of your ssh public key into this file, then save the file and close the text editor. After this, update the permissions for authorized_keys:

Configure permissions for the ssh key
chmod 600 ~/.ssh/authorized_keys

Update ssh config

On your local machine update the ssh config file again:

~/.ssh/config
Host awesome-vps
Hostname 5.75.228.235 # change to your ip address
User metters
IdentityFile ~/.ssh/{name of the private ssh key}

If you still want to connect as root, put the root as username before the hostname:

Connect to your vps as root user
ssh root@awesome-vps

Now you can connect via ssh as root (or user metters, if you followed the previous optional steps):

Connect to your vps using the configured host name
ssh awesome-vps

The following steps assume you are connected as non-sudo user. Therefore, the sudo keyword is required for many commands.

Set up Firewall (UFW)

Execute the following commands, one after another:

Commands to configure the firewall from the terminal
sudo ufw disable # turn off firewall
sudo ufw default deny incoming # block all incoming traffic
sudo ufw default allow outgoing # allow all outgoing traffic
sudo ufw allow ssh # allow incoming traffic on port 22
sudo ufw allow http # allow incoming traffic on port 80
sudo ufw allow https # allow incoming traffic on port 443
sudo ufw enable # turn on firewall

To check the firewall configuration execute sudo ufw status or sudo ufw status verbose to check the ufw configuration. It should print something like this:

Get status and configuration of your firewall
sudo ufw status
Basic setup for your firewall
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443 ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)

Set up web server: Caddy

Download and install

First, run sudo apt update && sudo apt upgrade, to install available updates.

After this, install Caddy. The vps is an Ubuntu instance, there is a section about the installation on Ubuntu.


Click to see the commands to install Caddy (its latest stable version)
Download and install Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Regularly used commands to manage Caddy

Basic command to manage Caddy
sudo systemctl status caddy # print the current status of Caddy
sudo systemctl start caddy # turn Caddy on
sudo systemctl stop caddy # turn Caddy off
sudo systemctl reload caddy # restart Caddy (necessary after editing the Caddyfile)

Configure Caddyfile

Configure CaddyServer via Caddyfile. Usually, the file is in /etc/caddy/:

Create Caddyfile
sudo vim /etc/caddy/Caddyfile

This is nearly the most basic setup for your site

/etc/caddy/Caddyfile
blog.metters.dev, www.metters.dev, metters.dev { # (sub)domains that should point to your site
root * /var/www/html/metters.dev # location of your artefact
file_server
}

Remember to restart Caddy after editing the Caddyfile with the reload command sudo systemctl reload caddy!

Add a static site

In order for Caddy to serve your site, you must create a folder which contains the build artefact or contents of the website (which can also be a simple HTML file):

Create a folder and your static site
sudo mkdir -p /var/www/html/metters.dev # the name of the last folder does not have to match the domain/url, but it is helpful, when hosting several sites
sudo echo '<h1>Hello World!</h1>' > /var/www/html/metters.dev/index.html # this is where the build artefact is stored, in this case a single HTML file

Configure DNS record

Add an A record for every domain/subdomain that you need. With a Caddyfile as the one shown above that would be three entries: @, www, and blog, with the latter two as subdomains.

Add entries to DNS record as needed. Change ip address to your own!
@ IN A 5.75.228.235 ➡️ metters.dev
www IN A 5.75.228.235 ➡️ www.metters.dev
blog IN A 5.75.228.235 ➡️ blog.metters.dev

Use the following command to find out what ip address the domain refers to. As soon as the displayed ip address matches the one you configured in your DNS record, open the previously configured domain in your browser.

Query the ip your domain points to
dig +short blog.metters.dev. # do not forget the dot at the end
The configured ip in your DNS record
5.75.228.235

Resources