Having a personal website with a custom domain to write my thoughts, document my learnings, and showcase my projects has always been a long-time goal of mine. I recently decided to follow through on that goal and bought a domain name, along with a VPS (Virtual Private Server) subscription, to host my website. Why VPS when there are cheaper and easier solutions? Well, because I want to learn how to host my own website from scratch.
To create this website, I researched a lot of options I could use, from the cloud provider to the tech stacks to use. For the cloud provider, I initially wanted to use DigitalOcean due to their “free trial” with limited credits. Unfortunately, they suspended my account right after I entered my credit card information. From all the information I gathered on the internet, they have a tendency to block accounts that use a new credit card as a fraud prevention measure. Deciding that contacting them would require too much effort on my part, I chose a local cloud provider in my country, idcloudhost. As for the website tech stacks, I had a few options from the recommendations of others on the internet and decided to use Hugo since I only need a static website for this project of mine.
In my first post, I want to document the steps I took to host my website. Below are the tools I’m using:
- Linux based OS for the server
- Hugo as a site generator
- Apache 2 as web server
- Git
A little about Hugo
Hugo is a static website generator. It’s fast, simple, free, open-source, and supports multi-language websites. Hugo also has pre-built themes that will help me create my website faster without the need to delve too much into the technical side of web development. The good thing is, if I want more customization, I can also create my own template in the future. Overall, it is not too hard to set up.
Installing Hugo
I installed Hugo on my Linux VPS for production and on my local PC for development. On the VPS, I installed it using the prebuilt binaries, while I used Docker on my local PC. Before installing anything, I created a folder to store the downloaded file in my home
directory.
|
|
Installing Prerequisites
While these software are not required for all cases, I decided to install all of them for future usage:
- Git
- Go
- Dart Sass
Before doing anything, I ran sudo apt update
to update the available packages list information in my server.
Installing Git
For Git, I just installed it using apt install
. Then, I validated the installation by running the git version
command.
|
|
Installing Go
I went to the official Go website (https://go.dev/doc/install) to get the installer download link. Then, I downloaded the package with wget
.
|
|
Next, install go with the command below:
|
|
Add these lines to $HOME/.profile
or /etc/profile
(for a system-wide installation). I used vi as my text editor. Here’s how it looks:
|
|
Lastly, re-login to apply the changes of the profile file and verify the installation by running:
|
|
Installing Dart Sass
Go to the Dart Sass release page, choose the version (I used version 1.66.1), and copy the link. Download it with wget
:
|
|
Next, extract the archive file to ~/installer/
directory.
|
|
Move the extracted dart-sass
directory to the /usr/local/
directory.
|
|
Add these lines to $HOME/.profile
or /etc/profile
(for a system-wide installation). Here’s how it looks:
|
|
Finally, re-login to apply the changes of the profile file and verify the installation by running:
|
|
Installing Hugo - Prebuilt Binaries Installation in Linux
Go to this website: https://github.com/gohugoio/hugo/tags
Download the version that is going to be used. For my website, I’m using Hugo version 0.119.0.
1 2 3 4 5
# Command (Run in Terminal): wget <download_link> -P ~/installer/ # Example: wget https://github.com/gohugoio/hugo/releases/download/v0.119.0/hugo_extended_0.119.0_Linux-64bit.tar.gz -P ~/installer/
Extract the archive file
1 2 3 4 5
# Command (Run in Terminal): tar -xvf <archive_file_name> -C ~/installer/ # Example: tar -xvf ~/installer/hugo_extended_0.119.0_Linux-64bit.tar.gz -C ~/installer/
Move the “hugo” executable file to
/usr/local/bin/
. This directory already exists in Linux PATH environment variable, so there is no need to add it again later.1 2
# Command (Run in Terminal): sudo mv hugo /usr/local/bin/
Run this command to confirm that Hugo is installed correctly.
1 2 3 4 5 6 7 8 9 10 11 12
# Command (Run in Terminal): hugo env # Output: hugo v0.119.0-b84644c008e0dc2c4b67bd69cccf87a41a03937e+extended linux/amd64 BuildDate=2023-09-24T15:20:17Z VendorInfo=gohugoio GOOS="linux" GOARCH="amd64" GOVERSION="go1.21.1" github.com/sass/libsass="3.6.5" github.com/webmproject/libwebp="v1.2.4" github.com/sass/dart-sass/protocol="2.1.0" github.com/sass/dart-sass/compiler="1.66.1" github.com/sass/dart-sass/implementation="1.66.1"
Installing Hugo - Docker Container Configuration in Windows
For development, I use a Docker container on my Windows machine. First, I installed Docker Desktop on Windows, following the Docker official documentation for this. Then, I created a custom Docker image using this Dockerfile:
Setup Dockerfile
|
|
The Dockerfile provided above will build a Docker image with the following specifications:
- Ubuntu 24.04 as operating system
- go 1.21.1
- hugo extended 0.119.0
- dart-sass 1.66.1
The version of all the listed software can be changed based on needs by modifying the software download links in the Dockerfile.
Lastly, build the Docker image with this command:
docker build -t de-hugo-dev .
Setup Docker Compose File
This Docker compose file will be used to run a Docker container and serve the website with hugo server
, using the existing project located at ./srv/http/src
.
|
|
Note:
--poll 700ms
was added to solve Issue 1
Running Hugo in Development
Creating a New Project
To create a new Hugo project using Docker, I run this command:
|
|
The command above will create a new Hugo project and put it inside ./srv/http/src/
on my host. The container will then be immediately stopped and deleted. This part will only be run once to create a new project. For existing project, I will use docker compose
to create and run the container.
Run the Docker Container and Serve the Website
To run the Docker container:
|
|
It will run the development site. Every edit made in the folder ./srv/http/src
will be reflected live on the site (http://localhost:1313).
Publish the Site in Production
Edit the Hugo configuration file (config.yml
, hugo.yaml
, hugo.toml
, or hugo.json
- I prefer to use the config.yml
file) and make the following changes:
- Set the baseURL for the production site to the VPS domain name.
- Set the title of the production site.
- Set the preferred region and language.
|
|
To publish the site, run:
hugo
It will generate the ready-to-publish site in the public
directory. Then, on the VPS, create the directory /srv/http/your-website-name
. Copy the generated public
folder from development to /srv/http/your-website-name
in production.
Next, edit the Apache2 configuration file:
|
|
Comment out these lines:
|
|
and add the following lines:
|
|
Save the file.
Then, edit the default virtual host configuration file:
|
|
Change these lines from:
|
|
to:
|
|
Finally, restart the web server:
|
|
Access the website with the domain name using a browser. Note that, the web server will serve “Page Not Found” since the Hugo project is still empty.
Issue Troubleshooting
Issue 1 File Changes on Host Not Reflected in Hugo Web Site
Issue | When editing development files on the host, the expected behavior was for changes to be immediately reflected at http://localhost:1313. However, although the file changes on the host were successfully reflected in the container, Hugo did not update the website when changes were made. |
Cause | Common issue in windows host. Based on this source:"In cases where Hugo CLI is running in a shared directory on linux VM on a windows host the dev server is not detecting file changes from the host environment. (ex: Whenever a docker dev environment is running on a windows host.). This is because by default most watchers will use native file system change detection. And it is not working well in this shared situation." |
Solution | Added --poll 700ms to the command in the compose file when running "hugo server". |