Serving Octopress From a Self-hosted Git Repository

There are two good reasons to serve Octopress from a self-hosted git repo.

  1. It provides you with an off-site backup in case your local copies go up in flames.
  2. It gives you an environment where you can integrate secondary scripts and libraries that allow you to do things like e-mail posts to Octopress.
  3. git provides a very efficient, and atomic, means of uploading your files.

Complicating factors:

  1. Not all ISPs have ssh access or the latest version of ruby, and may not have git or Bundlr installed.
  2. Because of the above you may not be able to regenerate html files from markdown on the server.
  3. You need to have a basic familiarity with navigating directories and editing files over ssh.

These instructions are not for people uncomfortable with using the command line in a *nix environment. Of course, if you were uncomfortable with that you probably wouldn’t be using Octopress in the first place.

In order to do this you must have ssh access to your server, and have git installed on it. In order to generate new files on the server you must also have Ruby 1.9.2 (or higher) and Bundlr installed You must also have a basic understanding of how to get around on a Linux command line. This is not uncommon these days, but in order to check just ssh in and type:

which git
which bundle

If they both return a path to the executable you’re all set. If git is installed we can proceed without problem. If git and bundler are installed and ruby is at 1.9.2 we can generate the html on the server. For those not already familiar, which xxxx will print out the path of the executable if it is installed and on your path. If it returns immediately with no output it is either not installed or not on your path.

Next check the ruby version:

ruby --version

Octopress requires a recent version of Ruby to run, so if it says anything lower than 1.9.2 you will not be able to generate files on the server. Talk to your ISP about installing a more recent version of Ruby. You will, however, still be able to use git to transfer files to and from your server, serve from its generated directory, and act as a backup.

There are a couple ways to make this work. You could just host a non-bare repo on the server and set receive.denycurrentbranch to ignore so that you can push to it, but for security reasons, and safer removal of deleted posts it’s better to use a detached work tree.

SSH to your server, and create a new bare git repo somewhere that will not be served to the public.

#on the server
mkdir octopress_blog.git
cd octopress_blog.git
git init --bare
# core.worktree should point to a directory where 
# everything will be checked out into, but not 
# visible to the public. You must create this 
# directory. Git will not create it for you.
git config core.worktree /path/to/staging/directory

Once you’ve done that we need to set up the post-receive hooks. Create a hooks/post-receive file (in your bare repo) and make it look like this.

#!/bin/sh
# checkout the files after they've been pushed here
GIT_WORK_TREE=/path/to/staging/directory git checkout -f

Next, make it executable

chmod 755 hooks/post-receive

Later we’ll tweak hooks/post-receive to auto-generate the site (if you’ve got the pre-requesites on your server) but for now we just want to make sure we can push to it successfully.

Back on your local computer:

cd my_octopress_blog
git remote add live me@example.com:/path/to/octopress_blog.git

Now try pushing to it:

git push live master

It doesn’t have to be the master branch but that’s what most people use so we’re working from that assumption. It should push without problem, but pay careful attention to lines starting with “remote:”. If it does have problems, you should be able to figure out what’s wrong from the error message. At the end of the push you should see a message from remote saying “remote: Checking out files:…”

Back on your server take a look in the directory you pointed core.worktree at and see if your blog has been checked out there. From here on out we have to divide the instructions for people who can generate files on the server, and people who can’t.

To generate server-side or not….

Generating HTML server-side

Generating the html files server-side is good because you can set up things like JekyllMail to allow you to post to your Octopress without having to have a local checkout (great for blogging from work). Another benefit is that your “public” directory doesn’t have to be a subdirectory of your Octopress checkout. You can tweak your _config.yml to generate it anywhere on the server which can make server configuration easier for some. If you do change the destination attribute in your _config.yml on the server to a path that differs from your local version you’ll have to be careful to not commit any local changes to it.

Caveat Emptor: On shared servers there is one gotcha that you’ll have to be aware of. Long-running scripts are frequently auto-killed by the server so that one user doesn’t consume an unfair amount of resources. Until such a time as Octopress is capable of regenerating only the new posts there is the possibility that large blogs with hundreds of posts may exceed this limit and the generate script may be killed before completion.

Generating HTML locally

The only notable advantage to generating locally and pushing to a server-side repo is it works on servers where you have git but can’t run Octopress’ generate script for whatever reason.

Serving from a self-hosted git repo without generating HTML files server-side.

This is the simplest way to go, but it requires one change to the standard Octopress workflow, and the ability to point your server at a specific directory to serve your site from. In your workflow you’ll need to commit everything in your public directory each time your run rake generate, then push to the server. You need to be sure to add & commit any files deleted from your public directory after generation too, or else a deleted post will not get deleted on the server. Typically you can just say git add public each time and it’ll take care of managing the deleted files too.

Since the HTML files are going to be generated in a subdirectory of your Octopress repo (typically called “public”). You’ll need to point your server to the public directory under the directory that your core.worktree option is pointed at. Feel free to rerun the command above that set it if you want to change it to a different place now that you know everything works.

Once you’ve pointed your server to the appropriate directory all you will need to do to update your site and create an offsite backup of the original markdown files (and everything else) is to locally run

git push live master

Serving from a self-hosted git repo and generating HTML files server-side.

You’re going to follow the same steps as serving without generating files except you won’t be committing the “public” directory’s contents.

You’ll also need to add the following to the end of your hooks/post-receive

cd /path/to/staging/directory bundle install bundle exec rake generate

Note, if you’re using RVM or a custom gem path you may need to add those to the start of the hooks/post-receive script so that git will have it in its environment when it runs. For example, on Dreamhost you would have to add something like this to the top (assuming you’d already installed RVM and the current version of Ruby):

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*
GEM_PATH=$GEM_PATH:/home/my_username/.gems
PATH=$PATH:/home/my_username/.gems/bin

Make a commit locally, push it to the new repo, and confirm that your changes have been pushed to the staging directory and that rake generate has done its job and placed the new html files in your “public” directory (or wherever your _config.yml has destination pointing to).

Once you’ve confirmed that that works you can (optionally) change the /path/to/staging/directory to point to somewhere that isn’t just for testing in the hooks/post-receive file and in the core.worktree setting. All that’s left is pointing your server at whatever directory your _config.yaml’s destination is pointed at.

From then on you just run

git push live master

on your local machine to update your blog.