Publishing to the web with org-mode and git

Table of Contents

1 The problem

Over the years I've used various methods for putting my ramblings online. Blogging software like Wordpress, Google+, custom static html, Github pages and even pastebin. However all these methods had their fault.

Wordpress is a large application which does require some non-trivial resources. Furthermore it's tricky to work with in a distributed manner. I like to work in short bursts, which means there are lots of half-finished documents everywhere. This leads to a synchronization problem with the blogging software, often keeping the original text-file around and the published online version, resulting in all sorts of annoying version management issues.

Social media sites like Google+ are not ideal since you have absolutely no control over the presentation of your documents, which can change at the whim of the publisher. Furthermore they suffer from the same synchronization problem as blogging software.

Custom solutions are fine, I can use Vim; do whatever I want and then turn it into a web page. I played with these before site generators like Jekyll became a thing. The unfortunate downside is that it's still a bit of work to get it right. and this was before markdown became bigger than sliced bread; so it also meant writing HTML, which I detest.

2 The solution

Enter my own custom solution with org-mode, git and a custom stylesheet.

2.1 org-mode

Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.

org-mode is an Emacs 1 plugin which is literally the best thing ever. Apart from an excellent TODO list manager it also comes with a whole range of authoring tools. Basically you write your document in a markdown-esque way, and then export it to whatever. It has build in support for PDF (via LaTeX), ODF, and HTML. Since it's written by nerds it comes with all sorts of nice things. Build in support for source code:

(defun org-xor (a b)
   "Exclusive or."
   (if a (not b) b))

Formulas (through LaTeX rendered images, or optionally MathJax)

\begin{equation} x=\sqrt{b} \end{equation}

Tables, images and extensive support for Literate Programming with babel (see this cool Clojure example).

Org-mode ≥ 8.0 is recommended to make full use of the html export capabilities.

2.2 git-web and nginx

To overcome the versioning issues I normally use git. So the idea was to make use of a system that can serve git files over HTTP, optionally with their commit SHA. There are a couple of tools for doing so, like Indefero and Gitlab. However the most lightweight way seems to be gitweb (instaweb) which comes bundled with git itself.

The nasty thing however is that gitweb has fairly ugly URLs, and is dirt slow. So I used nginx as a reverse proxy to do the URL rewrite and caching.

Here's how to set it up:

First Create the folder which you want to server mkdir files.git; cd files.git. Setup a bare git repository on your server with git init --bare. Then start git-web git instaweb start --httpd=webrick --port 9990 2. And finally configure nginx with the following config:

http {
  # caching options
  proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=files:8m
                                               max_size=1000m inactive=600m;
  proxy_temp_path /var/cache/tmp;

server {
  listen 80;


  root /var/www/static;

  # The stylesheets and other static content can be served without gitweb
  location /static {
    expires max;
    access_log off;

  # Rewrite /f/commit/[sha]/[path]/[to]/[file]
  rewrite "^\/f\/commit\/([0-9a-f]{5,40})\/([\/\S+]+)$"
          "/?p=files.git;a=blob_plain;f=$2;hb=$1" last;

  # Rewrite /f/[path]/[to]/[file] (gets the HEAD)
  rewrite "^\/f\/([\/\S+]+)$"
          "/?p=files.git;a=blob_plain;f=$1" last;

  # Proxy to gitweb and set the cache for 60 minutes
  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://localhost:9990/;
    proxy_redirect http://localhost:9990/ https://$server_name/;
    proxy_cache    files;
    proxy_cache_valid  200  60m;
    proxy_cache_use_stale  error timeout invalid_header updating
                           http_500 http_502 http_503 http_504;


Optionally you can make gitweb pretty with some custom theme. This allows you to git commit and push and serves the files nice and fast.

2.3 stylesheet

Org-mode doesn't come with a really good stylesheet so I made a simple one (source), with most of the typography stolen. The sheet also contains a responsive rule for increasing the font size on mobile devices:

@media only screen
and (min-device-width : 320px)
and (max-device-width : 568px)
and (orientation : portrait) {
    html {
        font-size: 140% !important;
    body {
        width: 100% !important;
        padding: 1em !important;
    p {
        text-align: justify;
    #table-of-contents {
        display: none;

3 Conclusion

3.1 The good

It does plain text with git so it's very portable. The site looks half decent and its possible to link people to specific revisions. Since it's org-mode the documents can also trivially be exported to PDF or some other paper-friendly format.

3.2 The bad

It still doesn't make a nice index of everything I've ever made, but this can be build trivially. I still need to set-up https, and possibly some analytics.

Perhaps the most annoying thing is that all the documents are in the same repository right now, this is a bit of a compromise since making a seperate repository for each document would be overkill. However it does mean that the commit logs are not pristine.

All in all I'm fairly content for now and I'll probably end-up using pandoc to import some older work.




I use Evil since "I'm an addict" but love Lisp


Note that I'm using Webrick here, but it also has support for Apache and others

CC0 2013-11-08 JoelKuiper