Zerosum dot org
Published in

Zerosum dot org

A Pure Git Deploy Workflow (with Jekyll and Gitolite)

Manually deploying static site updates is so 2003. And Capistrano is great, but it’s an extra step you don’t always need (and somewhat overcomplicated). If you’re using Jekyll to power your site and already have your data in Git, it’s pretty easy to set up a pure Git deployment workflow. Even if you’re not using Heroku ;-).

By using either Gitosis or (better yet) Gitolite, you can be autodeploying like a boss in just a few minutes. And, because Gitolite allows you to manage repository access across ad-hoc groups, you can easily allow other users to push to your remotes and update the appropriate sites without them needing shell access on your server. And share those users across multiple projects, and so on and so on.

Here’s how I set up the deploy recipe that I use for updating this blog and a couple other static sites that I’m hosting on the same box.

Gitolite Installation

First, install Gitolite on the host you want to deploy to. Sitaram’s installation instructions are really easy to follow, but I’ll summarize here (I used the root method, which I suspect is what most readers would want to use too):

git clone git:// gitolite-source
cd gitolite-source
git checkout v1.5.6 (or whatever tagged version you want)
mkdir -p /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks
src/gl-system-install /usr/local/bin /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks

You’ll need to add a git user, who will own the repositories on the system. Create one now, and finish the Gitolite installation process:

adduser git
su - git
gl-setup /path/to/your/public-key/

Gitolite Administration

Make sure that you gave gl-setup the correct public key from your local (client) system. Now, on your client, you should now be able to check out the gitolite admin repository, through which you can manage users, groups, and repository access.

git clone git@my-server:gitolite-admin

Next, edit gitolite-admin/conf/gitolite.conf. Gitolite comes with a couple pre-defined group definitions (including one for the admin repository; don’t screw that one up). Adding a user permissions for a new repository goes like this:

repo  blog
RW+ = kyle stan

Where blog is the name of the repository you want to create, and kyle and stan are two users who will need to be able to read and write to that repository. We have no way to identify Stan and Kyle yet, but that’s the next step. We’ll add their public keys to the gitolite-admin/keydir directory. Make sure to name them and

Add the new files to the git repo (git add .) and commit everything. Push it to the remote with git push origin master. This should push the gitolite admin changes back to your remote server and update the internal configuration, allowing Stan and Kyle to work on the blog project.

Managing a Static Site

For simplicity’s sake, let’s say that you’re Stan (Stan’s public key is your public key). To create the new repository and register it with Gitolite, you should now be able to do the following:

mkdir blog
cd blog
git init
touch index.html
git add .
git commit -a
git remote add origin git@my-server:blog
git push origin master

Instead of just creating an empty index file, make it a simple html file, or drop in a basic Jekyll site. Or, instead of creating a brand new repository, just add a remote (call it production perhaps?) to an existing Jekyll repository that you’ve been working on. In any case, when you push those commits up, Gitolite should check that your public key is authorized to perform that operation, and (assuming that it is) create the new bare repository for you automatically. Because it’s cool like that.

If you have a problem with this step, check that (a) your public key is indeed mapped to a username that is authorized to RW that repository, and (b) that the name in your gitolite.conf matches the public key filename.

If that all appears to be working, your next step will be to create a virtual host entry in your Apache config on the server (or your web server of choice). Here’s a basic Apache vhost:

<VirtualHost *:80>
DocumentRoot /var/www/blog

ErrorLog /var/log/apache2/blog-error.log
CustomLog /var/log/apache2/blog-access.log combined

And of course, create the docroot destination and make sure the git user owns it and can write to it. Then you can enable the config and restart Apache (or nginx or whatever).

Auto-Update on Deploy

I’m going to assume that we’re working with Jekyll here. And you should be, because it’s awesome. But if you are using some other static site generator, you can customize it pretty easily. The basic idea is that you want a post-receive hook to fire whenever the server receives a new push, and you want to regenerate the site data and redeploy it for everyone to see. This makes creating a new blog post really easy, for example.

Navigate to /home/git/repositories/blog.git/hooks and edit the post-receive file. You may also need to chmod ug+x it so it’ll execute properly. Here’s what goes inside:


cd $TMP_GIT_CLONE && jekyll --no-auto $TMP_GIT_CLONE $PUBLIC_WWW
cd ~ && rm -rf $TMP_GIT_CLONE

find $PUBLIC_WWW -type f -print0 | xargs -0 chmod 666
find $PUBLIC_WWW -type d -print0 | xargs -0 chmod 777


Save that, and then commit a sample change from your client and push it to the remote. The server should fire the post-receive hook automatically when the push has finished, and regenerate the site, dumping the changes into your docroot directory for the world to see. In fact, you should even see the Jekyll generation output in the git push command output, which makes it extra easy to troubleshoot in case you’re having a problem.

And voila, git auto-deploy goodness.

What’s best about this is that it’s easy to authorize new users to update a group site or blog, and manage those users all through their public keys and a simple config file. This config also makes it easy to spin up new static sites on the same host; just copy a couple configs and you’re good to go (or even better, investigate dynamically configured mass virtual hosting).

If you’re self-hosting your blog with some custom blog package you should really give this type of setup a look. I like it because it’s simple to manage access, incredibly quick to deploy updates, and everything is naturally stored in Git and in plaintext, so you’ve always got full revision history and never have to worry about vendor lock-in or proprietary formats. Need comments? Use Disqus. Less moving parts is almost always better. Simple is good.

Originally published at on November 1, 2010.



evolution through a series of accidents

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Nick Plante

Just another hacker working on OSS, UX, ML, HCI, BTC, ETH, and DeFi. VC and R&D at @dlabvc (@sosv). Hates acronyms.