GitOps website deployment with Gitea

3 minute read

I’m slowly simplifying and minimizing my home server environment for no particular reason other than enjoying the KISS principle.

To give some examples I am currently migrating repos from my old Gitlab instance to a new Gitea server I’ve set up on FreeBSD. The benefit is a platform that runs well on a significantly smaller VM, and where the key product actually does get updated by the operating system’s built-in package manager. Gitlab was notoriously bad at the latter.

In parallel with that, I’ve switched my web site from Wordpress to one that’s statically generated by Jekyll. In this case the migration was basically triggered by boredom.

One nice thing about Jekyll, is that you can have the contents of your entire site stored in version control: All pages are generated from Markdown files by the jekyll executable, and the resulting output can be presented by any webserver - I use Nginx.

Of course it would be nice to have a simple workflow to add and update posts and web pages on the go, and following the KISS principle, I decided to use git hooks to solve this need.

Please note that the following solution is based on a Gitea server running on FreeBSD. The principles are applicable on Linux too, but specific paths may differ.

What we need to achieve

We want a git push to trigger a new version of the web site to be generated based on what’s currently in the site repository, and then the finished site to be deployed to the web server, at a location that will be presented to the web.

Configuring the web server

I’ve set up a very basic web server running Nginx on FreeBSD. I’ve configured it to present its root from /usr/local/www/oxcrag.net.

To enable the necessary automation, I added a user, deployer, I created the directory _site in its home directory, chown‘d the directory so its group ownership was set to www, and finally I symlinked /usr/local/www/oxcrag.net to /home/deployer/_site.

Configuring the Gitea server

I started by installing the ruby and rubygem-bundler packages, which are necessary for Jekyll to work.

Next step was to switch to the git user and create an ssh key-pair. I then used ssh-copy-id to allow the git user to authenticate without a passphrase as the deployer user on my web server.

Git hooks are not enabled by default in Gitea, as a security precaution. In my case I accept the risk, so I added the following line to the [security] section of /usr/local/gitea/conf/app.ini:

DISABLE_GIT_HOOKS=false

After restarting the gitea service, a Git Hooks menu option turned up in the project settings.

The relevant hook for this purpose is post-receive, and I edited it to add the following little script:

#!/usr/local/bin/bash -l

export HOME=/usr/local/git

# Install Ruby Gems to ~/gems
export GEM_HOME=$HOME/gems
export PATH=$GEM_HOME/bin:$PATH

if [ ! -d $HOME/tmp ]; then
    mkdir $HOME/tmp
fi

WEBSERVER=websrv2.oxcrag.net
TMP_GIT_CLONE=$HOME/tmp/oxcrag.net
GEMFILE=$TMP_GIT_CLONE/Gemfile
PUBLIC_WWW=$TMP_GIT_CLONE/_site

git clone $GIT_DIR $TMP_GIT_CLONE
BUNDLE_GEMFILE=$GEMFILE bundle install
BUNDLE_GEMFILE=$GEMFILE bundle exec jekyll build -s $TMP_GIT_CLONE -d $PUBLIC_WWW

scp -r $PUBLIC_WWW deployer@$WEBSERVER:

rm -Rf $TMP_GIT_CLONE
exit

To keep the script under some sort of version control, I simply added a git_hooks directory to my site repository and created a post-receive file in it, with the above contents. In this specific case, the lack of automation between what’s in my repo and what actually gets run on the server is a good thing from a security perspective.

Once this was in place, I could verify that it all worked by making changes to the site repo and pushing the committed changes, whereupon my web site updated as expected.

The added benefit here is that I don’t need to use my laptop to update pages: Anything that can log on to my Gitea instance and browse the website repository via the Gitea web UI can also add new pages or modify existing ones: These changes to the repository also get treated as a git push, and therefore trigger the Git post-receive hook.