Deploying Sytes

Deploying Lisp applications got a lot easier using two great utilities: sb-daemon (a SBCL-specific package that makes it easy to run CL processes as daemons) and buildapp (a CL tool for creating executables, again, SBCL-specific). If you're using a different Common Lisp implementation, then this page won't be too useful (sorry, I settled on SBCL and I don't have experience with other implementations).

Note that, although the Common Lisp source is not required on a production system, the templates are needed at runtime. To make this really easy, I'm keeping the Git repositories on my server and I've set the Nginx handler to serve static files directly from Git (from a certain “static” directory).

I have installed SBCL on my server, along with Quicklisp, Buildapp and sb-daemon, simply because it was the easiest way to get started, but an alternative way would be to compile on another machine and pack only the executable + templates and static files to put them in production. I personally see no problem hosting the full development stack on the server.

Then I wrote the following update/build script:

#! /bin/bash

cd ~/GIT/cl-sytes
git pull
cd ~/GIT/
git pull
cd ~/GIT/syte.lisperator
git pull

buildapp \
    --asdf-tree ~/quicklisp/dists/quicklisp/software/ \
    --output ~/tmp/sytes \
    --load-system \
    --load-system syte.lisperator \
    --entry sytes::main

pkill sytes
sleep 1
mv ~/tmp/sytes ~/bin/sytes

Of course, adjust it to suit your needs. I'm loading two Sytes in the same Lisp image (one is this very website, and the other is (a project that was previously named ss-lisp). SBCL is not exactly lean on memory use, it would be quite wasteful to run multiple SBCL instance if you have multiple websites; however, when you can run multiple sites in the same Lisp image, then SBCL beats Apache by far. On my server SBCL occupies 85M of resident RAM right now, that's not too bad compared to 10-20 Apache processes holding on 50-70M each.

The “main” function

Sytes supplies a really dumb main function that daemonizes the process via sb-daemon. It looks like this:

  (defparameter *running* t)
  (defun main (argv)
    (declare (ignore argv))
    (sb-daemon:daemonize :output "/tmp/sytes.output"
                         :error "/tmp/sytes.error"
                         :exit-parent t
                         :sigterm (lambda (sig)
                                    (declare (ignore sig))
                                    (setf *running* nil)))
    (setf *running* t)
    (loop while *running* do (sleep 1))))

Feel free to write a more comprehensive one, should you need it.

Fork me on Github