I host my own website on a Linode VPS. At this point, I’ve forgotten all the details of how I set up my VPS a couple of years ago but the system has been rock solid. I’m pretty sure I followed guides like these ones.

I even get emails about the upgrades that take place on the server and generally things just work. I mostly manage this server by forgetting that it exists and creating a couple of git repositories every once in a while. Overall, I’m just happy to have a pretty cheap VPS that performs backups and that I mostly understand.

One downside to managing my own server, however, is that I don’t get to take advantage of cool Github actions or Gitlab CI. The features that these CI pipelines have out of the box are incredible. I know that I could host my own instance of Gitlab, Jenkins, Gitea + Drone CI, , but honestly, it just seems too heavy for my use case which is to run a single hugo command and copy some files to my production directory. So here’s my cheap, quick and dirty continuous deployment “pipeline” for building my website.

Git Hooks Link to heading

If you don’t know about git hooks, you should really go and read about them. Basically, hooks are custom code that run when certain git events are triggered. You can use these hooks to do all kinds of things:

  • Format code on commit
  • To check commit convention
  • Prevent rebases or merges
  • Send an email when certain branches update
  • Generate a static website

Disclaimer: Sometimes this can be really annoying. A pre-commit hook can make a commit fail if certain conditions aren’t met. You can always skip hooks with the --no-verify, but my general advice would be “just because you can, doesn’t mean you should.” Think of your team (or your future self) before going ham on the hooks. Furthermore, client-side hooks are generally non-enforceable (nor should they be from a security perspective). You’ll have to encourage your team to manually install the hooks. In short, there are usually better ways to accomplish automation. The use case presented in this post, in my opinion, is the perfect usage of hooks since it’s small, it’s personal and it only impacts me (in a positive way).

Hugo Continuous Deployment Link to heading

I’m hesitant to call this “continuous integration” since the amount of error checking isn’t very intelligent, however, it most definitely is continuous deployment. Every time an update is pushed to the site, the website will be rebuilt and deployed. Here is a post-receive hook that does the job for us. It’s also POSIX shell compliant (for people who care about that).

#!/bin/sh -e

# The production directory
# A temporary directory for deployment
# The Git repo

# Checkout the content to the temporary directory
rm -rf $TEMP
mkdir -p $TEMP
git --work-tree=$TEMP --git-dir=$REPO checkout -f
cd $TEMP
git --work-tree=$TEMP --git-dir=$REPO submodule update --init --recursive

# Generate site with Hugo

# Deploy website
rsync -av --delete public/ $TARGET

This script should be pretty self-explanatory, but at a high-level it performs the following

  • Sets error flag to exit script if any command fails1
  • Checks out the repository and submodules into a clean temporary directory
  • Attemps to generate the website
  • Deploys the website via rsync

  1. This will prevent blowing away your website if it fails to generate. But also, shame on you if you push code that doesn’t build ↩︎