Carving a little island on the internet - Part 1

Posted on Apr 20, 2024

While creating this blog, I thought this would be a good opportunity to self-host it instead of relying on a platform such as Gitlab Pages, or even a full-fledged blogging platform like Ghost. A bit of background about me: most of my experience as a software engineer is in systems, multimedia and graphical engineering, so this is a good opportunity to brush up on different skills, and to vary the tech I’m working with.

As a bonus, this will self-document my process and condense my learnings somewhere.

Prerequisites

For what I envisioned, I basically needed the following six elements:

A blogging framework & theme: I’m very much not a frontend person, so I needed a way to reduce work on that aspect to the minimum, and to ensure that writing and posting would be as low-friction as possible.

A hosting provider: for serving a static website, assuming I found a framework that was lightweight and fast, I wouldn’t need much. I’m not looking at some insane traffic (yet…), and I don’t need any compute power. Which means the free tier offers that most cloud providers have would be more than enough at first.

A web server: in order to serve the website over HTTP(S).

A domain name: this one is straightforward, I don’t want to pass an IP around.

A certificate: while technically not necessary, I still embarked on the journey with the goal of doing as much as I reasonably could to provide something clean and proper, and HTTPS shouldn’t really be an option these days.

A way to easily deploy changes: I’m a big fan of CI/CD and I consider it a must in any project, even though it’s technically not needed here, and I could just manually copy the files to the remote, but I value anything that lowers the energy eventually needed to advance a project, especially at a low cost.

Choosing while Keeping It Super Simple

After some research, weighing the pros and cons of the various necessary elements, I decided to base myself on the following:

Blogging framework –> Hugo: Looking myself into Go, having already seen this framework used in the wild, an after comparing the standard operations and concepts of a couple blogging framework, I settled on Hugo. Simple, fast, efficient, say no more. The Archie theme was also clean and efficient enough for me.

A hosting provider –> Oracle Cloud Free Tier: I liked the readability of the free tier offer better at Oracle Cloud. This doesn’t mean it’s necessarily the best, but given my current knowledge and requirements it matched.

A web server –> nginx: Having not-so-good memories of my experience with Apache a while ago, I thought I’d try on another well-known reliable webserver.

Domain name –> OVH: not particularly opinionated on my domain name provider, so I went with french, reputable, cost-efficient.

A certificate –> Let’s Encrypt: I’ve always seen Let’s Encrypt around, heard only good things about how easy to setup it was, so I settled on that and the certbot ACME client to quickly and easily setup HTTPS.

A way to easily deploy changes –> Gitlab CI/CD: Straightforward, as I use Gitlab to host my code already.

We’ll focus on setting up the first three in this post, the other three will be detailed in another.

Alright, let’s create a website

Before anything else, let’s have some simple, ready-to-test website. Creating a basic website with hugo is dead simple, using a custom theme:

hugo new site <name>
cd <name>
git init
git submodule add <url-to-a-hugo-theme-repository> themes/<theme-name>
echo "theme = '<theme-name>'" >> hugo.toml
hugo server

and off we go. Hugo will serve that site, by default on localhost:1313.

A very neat thing is that Hugo will detect changes you bring to the website (either its configuration or its content) and will live reload the site. I sometimes had to restart the server here and there, but for the most part this works well and allows for very fast iterations.

I won’t expand much on Hugo, but it’s rather simple to use overall, and it produces a static website in the public/ folder with a simple hugo invocation at the root of the repository.

A home for a small folder

Onto hosting that website. After signing up for Oracle Cloud, we have access to small Compute instances. These are fairly limited:

  • One physical core equivalent on a 2.0 GHz AMD EPYC™ 7551 (Naples) processor
  • 1GB of RAM
  • 0.48Gbps of network bandwith
  • Up to 200GB of block storage (instances must be created with a minimum of 50GB)

However, this will be largely sufficient to serve a small static website.

We first need to create a virtual cloud network (or VCN) and a subnet. Alongside an internet gateway, it will handle routing to our instances and enforce firewall rules on the traffic. There’s a simple VCN wizard that makes the creation very simple.

Then, we can create an instance, which will reside on our VCN and the subnet we’ve configured. We must pick a distribution, I went for a recent minimal Ubuntu1.

Some additional network configuration will be needed but we’ll look into that later.

Once the instance is up and running, we can ssh into it and get to work.

Setting up nginx to serve

To serve a website, whether over HTTP or HTTPS, we need a web server. This will basically listen on port 80 for HTTP (443 for HTTPS) by default, and handles all the incoming connections. For a static website, this simply means returning the pages and/or resources that have been requested via GET.

nginx is available as a package so let’s simply get it with sudo apt install nginx

Its configuration resides in /etc/nginx/. We’ll look at two folders, sites-available and sites-enabled. sites-available contains, in theory, one configuration file per website we want to serve. sites-enabled contains symlink to those config files, and is included by the main configuration file, nginx.conf. This means we can easily setup or teardown sites by adding or removing links in sites-enabled without touching their configuration files.

We’ll create a file for our website in sites-available, and configure the simplest HTTP server possible for now, using the following:

server {
    listen 80; # Listen on port 80 for IPv4
    listen [::]:80; # Listen on port 80 for IPv6

    # Must correspond to the Host header of the request
    # We can use wildcards, or the special name "_" to match everything
    server_name "example.com";

    root /home/ubuntu/www; # Where the website files reside

    # The index file within the root of our site
    index index.html;

    # Location directives route requests to the file locations in the filesystem
    # This directive matches all requests, and attempts to return the requested
    # file, possibly with a trailing /, and a 404 response otherwise
    location / {
        try_files $uri $uri/ =404;
    }
}

Once this is done, we’ll create a symbolic link in the sites-enabled folder with

cd sites-enabled && sudo ln -s /etc/nginx/sites-available/<config-file> <config-file>

nginx can now pick up our config file for this site, and will be configured to listen on port 80 and serve it.

All there is to do is to reload nginx with sudo nginx -s reload. We can test that nginx is configured properly by running

curl -H 'Host: example.com' localhost:80

The HTML of the response should be sent back by nginx.

We can now render our website with hugo, then copy the content of the public folder it generated for us into the root, /home/ubuntu/www, and nginx will happily serve that when requested.

Beyond localhost shenanigans

We’re done with the local part of the work. In a future post, we’ll look at how to make it available on the internet.


  1. There’s multiple other distribution that can be setup right away, including Oracle Linux, RHEL, CentOS, Windows, but I went with a distribution that I’m familiar with, even though it has its issues, especially in a low resources environment. I picked the minimal variant to curtail that somewhat but it still isn’t great. ↩︎