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.
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://github.com/sitaramc/gitolite 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/id_rsa.pub
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
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
blog is the name of the repository you want to create, 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
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> ServerName blog.superawesomedomainname.com ServerAdmin firstname.lastname@example.org DocumentRoot /var/www/blog
ErrorLog /var/log/apache2/blog-error.log CustomLog /var/log/apache2/blog-access.log combined </VirtualHost>
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.
/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:
GIT_REPO=$HOME/repositories/blog.git TMP_GIT_CLONE=$HOME/tmp/blog PUBLIC_WWW=/var/www/blog
git clone $GIT_REPO $TMP_GIT_CLONE 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.