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
TARGET="/var/www/devonmorris.dev"
# A temporary directory for deployment
TEMP="/tmp/devonmorris.dev"
# The Git repo
REPO="/srv/git/website.git"
# 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
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
-
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 ↩︎