Tech Blog Setup with Hugo

Introduction#

In this post, I’ll walk you through the step-by-step process of how I set up this site as a self-hosted blogging solution. This guide covers everything from creating a lean Alpine Linux container on Proxmox, configuring the Hugo environment with all necessary packages, to automating the build and deployment process via a custom script. I’ll also share tips on using Visual Studio Code (VSC) with SSH plugins to manage your container seamlessly.

This solution was born from a desire to have full control over my content and deployment process—without relying on external, often finicky, managed platforms. Let’s dive in!


Prerequisites#

Before you begin, make sure you have:

  • A Proxmox server to host your Alpine Linux container.
  • An Alpine Linux container running on Proxmox.
  • Basic familiarity with the command line.
  • Nginx installed on your Alpine server (or a reverse proxy configured on your home server) to serve your static site.
  • A domain (e.g., cliffixit.com) with DNS properly configured to point to your reverse proxy.
  • Visual Studio Code (VSC) installed with an SSH extension (such as “Remote - SSH”) to edit files on the container.

Step 1: Setting Up the Alpine Linux Container#

  1. Create an Alpine Linux container on Proxmox.
    • Log into the Proxmox Web UI: Open your browser and navigate to your Proxmox management interface. Navigate to Storage → Content: Click on “Templates” and download a minimal Linux template.

      (Tip: For the smallest footprint, choose Alpine Linux or, if you prefer a bit more familiarity, a Debian minimal template. Allocate minimal resources such as 1 CPU core and 256–512 MB of RAM.)

Detailed Steps to Create a New LXC Container

a. Click on "Create CT":
b. Start the container creation wizard.
c. General Settings: <br>
    - CT ID: Pick an available ID (e.g., 101).
      - Hostname: Something cool like my-blog-builder.        
      - Password: Set a strong password (or set up SSH 
        keys later if you’re into that).
d. Template Selection:
    - Choose the template you just downloaded (e.g., Alpine Linux 
      is used in our build).
e. Root Disk:
    - Set a small disk size (e.g., 2–4 GB) to keep the footprint 
      minimal.
f. CPU & Memory:
    - Allocate 1 CPU core and about 256–512 MB of RAM—more than 
      enough for content creation and running Hugo.
g. Network:
    - Configure your network settings (DHCP is usually fine but I 
      recommend setting a reservation, or assign a static IP if needed).
h. Confirm & Create:
    - Review the settings and click “Finish” to create the container.

Start and Access Your Container

a. Start the Container:
b. Select your new container and click “Start.”
c. Open the Console:
d. Use the built-in Proxmox console or SSH (if configured) to log in.

Log in to your container.

a. Use VSC with the 'Remote - SSH' plugin to connect to your container. 
This allows you to edit files directly on the server.

Step 2: Installing Required Packages#

Once you’re logged in, update the package index and install the necessary packages. Run the following commands:

apk update
apk add nginx hugo git glibc

(Both git and glibc are optional, however installing them and not using them has minimal impact on this lean build.)

Note: Hugo Extended is required by many themes (including the Terminal theme used on this) to compile SCSS. Verify by running hugo version. Also, you might need to manually install hugo if the version available in the repositories is older.

If necessary, you can manually install the latest version by checking GitHub for the latest version

  1. If you installed from the repository and it is an older version, remove via:
apk del hugo
  1. Download the Latest Hugo Extended Binary: (v0.143.1 is the latest as of today, however you should update accordingly)
wget https://github.com/gohugoio/hugo/releases/download/v0.143.1/hugo_extended_0.143.1_Linux-64bit.tar.gz -O hugo.tar.gz
  1. Extract the Archive:
tar -zxvf hugo.tar.gz
  1. Move the Hugo Binary to Your PATH:
mv hugo /usr/local/bin/hugo
chmod +x /usr/local/bin/hugo
  1. Verify the upgrade:
hugo version

Step 3: Configuring the Hugo Environment#

Set Up Your Hugo Site Directory#

Your site should have a structure similar to:

ClifFixIT-Reloaded/
  ├── archetypes/
  ├── content/
  ├── data/
  ├── layouts/
  ├── static/
  ├── themes/
  └── config.toml

Edit config.toml#

An example configuration might be:

baseurl = "" # Input your URL in quotes. e.g., "https://cliffixit.com/"
languageCode = "en-us"
theme = "terminal"
pagination.pagerSize = 5

[markup.goldmark.renderer]
  unsafe = true

[params]
  contentTypeName = "posts"
  showMenuItems = 2
  fullWidthTheme = false
  centerTheme = true

[languages]
  [languages.en]
    title = "My Awesome Blog!" # Change the title to something appropriate
    [languages.en.params]
      subtitle = "A simple, retro theme for Hugo"
      keywords = ""
      readmore = "Read more"
      readotherposts = "Read other posts"
    [languages.en.params.logo]
      logoText = "My Awesome Blog!"
      logoHomeLink = "/"
    [languages.en.menu]
      [[languages.en.menu.main]]
        identifier = "about"
        name = "About Me"
        url = "/about/"

Create a Sample Post#

Use Hugo’s archetype mechanism to create a new post:

hugo new posts/my-first-post.md

Then edit the file in VSC as desired.


Step 4: Automating Deployment with a Custom Script#

To streamline updating your site, I created a deployment script that:

  • Clears out old build artifacts.
  • Runs a Hugo build with the correct production baseURL.
  • Copies the generated files to the Nginx document root.
  • Reloads Nginx to serve the updated content.

Below is an example deploy.sh script that I saved to the root of the blog site:

#!/bin/bash
# deploy.sh - Automate Hugo build and deployment

echo "Cleaning up old builds..."
rm -rf public/ resources/_gen/

echo "Building the site..."
hugo --minify --baseURL "https://cliffixit.com/"
if [ $? -ne 0 ]; then
  echo "Hugo build failed. Aborting deployment."
  exit 1
fi

NGINX_ROOT="/var/www/cliffixit"
echo "Deploying to Nginx root: ${NGINX_ROOT}"
rm -rf "${NGINX_ROOT:?}"/*
cp -r public/* "${NGINX_ROOT}/"

echo "Reloading Nginx..."
rc-service nginx reload

echo "Deployment complete!"

Save the script in your Hugo site root (e.g., deploy.sh).
Make it executable:

chmod +x deploy.sh

Run the script to deploy your updates:

./deploy.sh

Step 5: Using Visual Studio Code for Remote Management#

I use Visual Studio Code with the Remote - SSH extension to edit files on the Alpine container directly. This setup allows for seamless editing of Markdown files, configuration files, and deployment scripts.

Useful Extensions:#

  • Remote - SSH: Connects to your container.
  • Markdown Preview: Provides a live preview of your Markdown content.
  • GitLens (optional): For Git integration if you decide to track changes with Git.

Conclusion#

This deep dive showed the full process of setting up a self-hosted blogging solution:

Code
  • Alpine Linux container creation on Proxmox.
  • Installing necessary packages via apk.
  • Configuring Hugo with a custom config.toml.
  • Automating deployment with a custom Bash script.
  • Managing content via Visual Studio Code over SSH.

Although I initially experimented with DigitalOcean’s automated deployment, its inconsistencies led me to develop this custom, script-based workflow coupled with an Nginx reverse proxy. This setup gives me full control and reliability over my site’s updates.

Thank you for reading this guide. I hope it helps you set up your own self-hosted blog with confidence. Stay tuned for more deep dives and technical insights!