Lisp Web Server From Scratch using Hunchentoot and Nginx

**************************I tried my setup on Ubuntu 13.10 server and had some issues so I decided to update this article, unfortunately I dont have the time to rewrite it so I am just hacking the bits that where issues on Ubuntu 13.10**********************

So you want your own lisp web server to hack away at but where do you start? Well don’t despair, what follows is a recipe for getting such a server running in no time. (Don’t worry you won’t be coding it from scratch you will be building it with a “software lego” set.)

Much of what you will see here was taken from different websites especially these two great articles http://blog.ponto-dot.com/2010/08/15/setting-up-common-lisp-on-a-web-server and http://blog.ponto-dot.com/2009/08/18/hunchentoot-behind-proxy-server. These articles do a better job of explaining what is afoot than my posting here. Please do go to these articles and show your appreciation for the hard work that went into them by leaving a comment.

All that I am doing is giving you an A to Z recipe in one place to get you going.

There are a lot of good articles in the slicehost articles collection as well that are worth a read on their own if you want to understand more about the iptables and stuff.

Just a note, when I supply a command for the command line I will prefix it with ~# for the server or ~local# for the local machine

First things first, lets set-up a secure server:

1. Install your slice from www.slicehost.com. A Ubuntu 10.10 (maverick meerkat) 32bit 512 slice should be enough. (Initially I thought a 256 slice would be enough but once sbcl gets used it reserves memory and if you don’t have enough spare for the GC to run then you end up with problems. Maybe one of the experts can enlighten us on what the reasons for this is and what the best way is to handle it. If budget is an issue you can limit the amount of memory sbcl reserves when you load it up. I suppose you need to experiment till you find then optimal settings for your set up.)

2. ssh into your new slice with the supplied password.

  ~local#ssh root@ip_address_of_server

3. Set up your locale.

  ~#locale-gen en_ZA.UTF-8
  ~#update-locale LANG=en_ZA.UTF-8
  ~#dpkg-reconfigure locales
  #Set the local time
  ~#dpkg-reconfigure tzdata

3. Update the server.

  ~#apt-get update
  ~#apt-get upgrade

4. Change the root password. Do I have to remind you to choose something sensible but secure enough?

  ~#passwd

5. Add a new user called admin and add it to the sudoers list. This is the user you will be using in the future to ssh into the server. We are going to disallow logging in as root later so you cant skip this step unless you want to experience locking your self out of the server later.

  ~#adduser admin
  ~#usermod -a -G sudo admin

6. Create a ssh key and upload your public key to the server. Dont leave the ssh passphrase blank!

  ~local#ssh-keygen -t rsa
  ~local#scp ~/.ssh/id_rsa.pub admin@ip_address_of_server:

7. Create the authorized_keys file and set its permissions correctly. ssh is very pedantic when it comes to permissions!

  ~#mkdir ~admin/.ssh
  ~#mv ~admin/id_rsa.pub ~admin/.ssh/authorized_keys
  ~#chown -R admin:admin ~admin/.ssh
  ~#chmod 700 ~admin/.ssh
  ~#chmod 600 ~admin/.ssh/authorized_keys

8. Next, let’s edit the /etc/ssh/sshd_config file and disable some defaults. Here are the adjustments you should make:

  ~#nano /etc/ssh/sshd_config

  Port 22222                           <--- change to a port of your choosing
  Protocol 2
  PermitRootLogin no
  PasswordAuthentication no
  X11Forwarding no
  UsePAM no
  AllowUsers admin

9. Set up IPTABLES to make server more secure.

/sbin/iptables -F
nano /etc/iptables.up.rules

Paste the following into the file:

*filter

#  Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

#  Accepts all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

#  Allows all outbound traffic
#  You can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT

# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

#  Allows SSH connections
#
# THE -dport NUMBER IS THE SAME ONE YOU SET UP IN THE SSHD_CONFIG FILE
#
-A INPUT -p tcp -m state --state NEW --dport 22222 -j ACCEPT

# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT

# log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

# Reject all other inbound - default deny unless explicitly allowed policy
-A INPUT -j REJECT
-A FORWARD -j REJECT

COMMIT

10. Tell the server to use the new rules.

  ~#/sbin/iptables-restore < /etc/iptables.up.rules

11. To make sure the iptable rules are automatically loaded in the future create the following script.

  ~#nano /etc/network/if-pre-up.d/iptables

#!/bin/sh
/sbin/iptables-restore < /etc/iptables.up.rules

  #Make the file executable
  ~#chmod +x /etc/network/if-pre-up.d/iptables

12. Now that we have our security in place lets reset ssh

  ~#/etc/init.d/ssh reload
  *****or for Ubuntu 13.10****
  ~#restart ssh

13. To test the ssh security settings run the following command from your local machine without closing the current ssh connection. If this command does not log you in automatically you have fluffed something along the way and need to recheck your settings!

  ~local#ssh -p 22222 admin@ip_address_of_server

Install and set up nginx:

(From here on we will be using the session where we logged in as admin so that is why there are sudo’s in the commands now.)

  ~#sudo apt-get install nginx
  ~#sudo nano /etc/nginx/nginx.conf

user www-data;
worker_processes  4;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
    # multi_accept on;
}

http {
    include       /etc/nginx/mime.types;

    access_log  /var/log/nginx/access.log;

    sendfile        on;
    tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  3;
    tcp_nodelay        off;

    gzip  on;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_types  text/plain text/css application/x-javascript text/xml
                application/xml application/xml+rss text/javascript;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Install and set up SBCL:
(Note: if you did not chose a 32 bit slice you must adjust the sbcl-1.0.43-x86-linux-binary.tar.bz2 download to the correct version.)

  ~#wget http://prdownloads.sourceforge.net/sbcl/sbcl-1.1.15-x86-64-linux-binary.tar.bz2
  ~#bzip2 -cd sbcl-1.1.15-x86-64-linux-binary.tar.bz2 | tar xvf -

  ~#cd sbcl-1.1.15-x86-64-linux

  ~#sudo apt-get install build-essential

  ~#sudo sh install.sh



To test sbcl run the following.
  ~#sbcl

Set up a hunchentoot user that will be used to run the hunchentoot/sbcl server process in detachtty:

  ~#sudo adduser --system --disabled-password --home /home/hunchentoot --shell /bin/bash hunchentoot

Install and setup Quicklisp as your lisp library repository and setup the infrastructure for your own projects.:

1. Set up Quicklisp.

~#curl -O http://beta.quicklisp.org/quicklisp.lisp

~#sbcl --load quicklisp.lisp

(quicklisp-quickstart:install)
(ql:add-to-init-file)
(ql:quickload "swank")
(ql:quickload "hunchentoot")
(ql:quickload "cl-who")

(quit)

  ~#sudo ln -s /home/admin/quicklisp/ /home/hunchentoot/quicklisp

Configure the web server as a process that can be started and stoped:

1. Set up the web server process.

Telnet is used to kill hunchentoot server
  ~#sudo apt-get install telnet 

  ~#sudo nano /etc/init.d/hunchentoot

(You can download the following script from http://blog.ponto-dot.com/2010/08/15/setting-up-common-lisp-on-a-web-server. 
Once again the cudos for this must go to the original author.)

#!/bin/sh
### BEGIN INIT INFO
# Provides:          hunchentoot
# Required-Start:    $all
# Required-Stop:     $
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Launch Hunchentoot
# Description:       Launch Hunchentoot with start-stop-daemon
### END INIT INFO
NAME=hunchentoot

. /lib/lsb/init-functions

hunchentoot_start()
{
    echo -n "Starting Lisp Hunchentoot Server process..."
    echo ""
    su - hunchentoot -c ~hunchentoot/start-hunchentoot
}

hunchentoot_stop ()
{
    echo -n "Stopping Lisp Hunchentoot Server process..."
    echo ""

    # 6200 is the port to signal end of lisp process
    (telnet 127.0.0.1 6200 &) > /dev/null
    (sleep 7)
}

hunchentoot_usage()
{
    echo "Usage: /etc/init.d/hunchentoot {start|stop|restart|status}"
    exit 1
}

hunchentoot_status()
{
    status_of_proc -p "/home/hunchentoot/run/$NAME.pid" "$NAME" $NAME
}

case "$1" in
    start)
        hunchentoot_start
        ;;

    stop)
        hunchentoot_stop
        ;;

    restart)
        hunchentoot_stop
        hunchentoot_start
        ;;

    status)
        hunchentoot_status
        ;;

    *)
        hunchentoot_usage
        ;;

esac

exit 0

  ~#sudo chmod +x /etc/init.d/hunchentoot

  ~#wget https://github.com/huetsch/detachtty/archive/master.zip
  ~#sudo apt-get install unzip
  ~#mkdir source
  ~#unzip master.zip -d source/
  ~#cd source/detachtty-master/
  ~#make
  ~#sudo make install
  ~#sudo install detachtty attachtty /usr/local/bin

  ~#cd ~hunchentoot
  ~#sudo mkdir log run
  ~#sudo chown hunchentoot:nogroup log run
 

  ~#sudo nano /home/hunchentoot/start-hunchentoot

#!/bin/bash

detachtty --dribble-file /home/hunchentoot/log/hunchentoot.dribble \
          --log-file /home/hunchentoot/log/hunchentoot.detachtty \
          --pid-file /home/hunchentoot/run/hunchentoot.pid \
          /home/hunchentoot/run/hunchentoot.sock \
          /usr/local/bin/sbcl --load /home/hunchentoot/init.lisp --userinit /home/hunchentoot/.sbclrc

  ~#sudo chown hunchentoot:nogroup start-hunchentoot
  ~#sudo chmod u+x start-hunchentoot

2. Set up how sbcl and hunchentoot will be initialised.
(Go to http://blog.ponto-dot.com/2010/08/15/setting-up-common-lisp-on-a-web-server to see the source of the script used here. Again thanx to Alex for his hard work.)

  ~#sudo nano /home/hunchentoot/init.lisp

(ql:quickload 'swank)

;; define some parameters for easier update
(defparameter *shutdown-port* 6200)  ; The port SBCL will be listening for shutdown
                                     ; this port is the same used in /etc/init.d/hunchentoot
(defparameter *swank-port* 4016)     ; The port used for remote interaction with slime

;; Start the Swank server
(defparameter *swank-server*
  (swank:create-server :port *swank-port* :dont-close t))

;;;Load websites
(ql:quickload 'my-home-project)

;;; We need a way to actually kill this baby so we
;;; setup a socket listening on a specific port.
;;; When we want to stop the lisp process we simply
;;; telnet to that port as run by the stop section
;;; of the /etc/init.d/hunchentoot script.
;;; This thread will block execution until the
;;; connection comes in on the specified port,
(let ((socket (make-instance 'sb-bsd-sockets:inet-socket
                             :type :stream :protocol :tcp)))

  ;; Listen on a local port for a TCP connection
  (sb-bsd-sockets:socket-bind socket #(127 0 0 1) *shutdown-port*)
  (sb-bsd-sockets:socket-listen socket 1)

  ;; When it comes, close the sockets and continue
  (multiple-value-bind (client-socket addr port)
      (sb-bsd-sockets:socket-accept socket)
    (sb-bsd-sockets:socket-close client-socket)

    (sb-bsd-sockets:socket-close socket)))

;;; Here we go about closing all the running threads
;;; including the Swank Server we created.
(dolist (thread (sb-thread:list-all-threads))
  (unless (equal sb-thread:*current-thread* thread)
    (sb-thread:terminate-thread thread)))
(sleep 1)
(sb-ext:quit)

  ~#sudo chown hunchentoot:nogroup init.lisp

Set up nginx to know about hunchentoot:

Here I had to tweak the code given in http://blog.ponto-dot.com/2009/08/18/hunchentoot-behind-proxy-server to get nginx to serve multiple virtual hosts correctly. This was mostly because hunchentoot does not have a concept for virtual hosts. The way to get one hunchentoot instance to serve multiple “sites” is to prepend the uri’s registered with hunchentoot with an identifier for the site. So when you register the home page for the domain myhome.com you would use /myhome/home.html. This is where nginx comes in because now we can tell nginx that if I get a request for myhome.com/home.html it should request /myhome/home.html from the hunchentoot server.

  ~#sudo rm /etc/nginx/sites-enabled/default

  ~#sudo nano /etc/nginx/sites-available/myhome.com

# Here we define a cluster of hunchentoot servers
# this can later be extend for load-balancing
# if we had more instances of hunchentoot. In this
# case i only have one instance running.
upstream hunchentoot {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name myhome.com;

    rewrite ^(.*)/$ $1/home.html;

    # General request handling this will match all locations
    location / {

        root /var/www/myhome.com/public/;

        # Define custom HTTP Headers to be used when proxying
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;


        # if the requested file does not exist then
        # rewrite to the correct hunchentoot path
        # so that the proxy_pass can catch and pass on
        # to hunchentoot correctly - proxy_pass
        # cannot have anything but the servername/ip in it
        # else nginx complains so we have to rewrite and then
        # catch in another location later

        if (!-f $request_filename) {
                rewrite ^/(.*)$ /myhome/$1 last;
                break;
        }

    }

    location /myhome/ {
        # proxy to the hunchentoot server cluster
         proxy_pass http://hunchentoot;
     }

}

  ~#sudo ln -si /etc/nginx/sites-available/myhome.com /etc/nginx/sites-enabled/myhome.com

  ~#sudo /etc/init.d/nginx restart

Set up my home project as an example:

  ~#sudo mkdir ~admin/source/my-home-project/
  ~#sudo nano ~admin/source/my-home-project/defpackage.lisp

(in-package :common-lisp-user)

(defpackage :my-home-project
  (:nicknames :my-home-project)
  (:use :cl :hunchentoot :cl-who)
  )

  ~#sudo nano ~admin/source/my-home-project/my-home-project.asd

(defpackage #:my-home-project-asd
  (:use :cl :asdf))

(in-package :my-home-project-asd)

(defsystem my-home-project
  :name "my-home-project"
  :version "0.1"
  :depends-on (:hunchentoot
	       :cl-who)
  :serial t
  :components ((:file "defpackage")	    
	       (:file "home-page" )))

  ~#sudo nano ~admin/source/my-home-project/home-page.lisp

(in-package :my-home-project)

(define-easy-handler (home-page :uri "/myhome/home.html")
    ()
  (with-html-output-to-string (*standard-output*)
      (str "This is my home page."))) 

  ~#sudo ln -s ~admin/source/my-home-project/my-home-project.asd ~admin/quicklisp/local-projects/my-home-project.asd

Start the server and view your handy work:

  ~#sudo /etc/init.d/hunchentoot start

Go to your browser and browse to http://myhome.com/ and you should see your home page.

When things go wrong!

To see if the server started up correctly see if sbcl and dettachy is running.

  ~#ps aux

The most likely place that things will go wrong is in your /home/hunchentoot/init.lisp file so check out the dribble file to see what happened in the start up.

  ~#sudo tail -n100 -f /home/hunchentoot/log/hunchentoot.dribble

If you can’t stop or start the server again after something went wrong do the following to clean up before starting the server again:

  ~#sudo tail /home/hunchentoot/run/hunchentoot.pid
  ~#sudo kill -9 
  ~#sudo rm /home/hunchentoot/log/hunchentoot.dribble
  ~#sudo rm /home/hunchentoot/run/*

Exstras:

For those of you that want to install a database do the following:
~#sudo apt-get install postgresql
~#sudo -u postgres createuser -D -A -P mynewuser
~#sudo -u postgres createdb -O mynewuser mydatabase

To use the db in lisp with postmodern have a look at http://marijnhaverbeke.nl/postmodern/

Home work:

Do your self a favour and read about how to connect to the sbcl instance remotely http://blog.ponto-dot.com/2010/08/15/setting-up-common-lisp-on-a-web-server.

Additional resources:

http://marijnhaverbeke.nl/postmodern/
http://weitz.de/hunchentoot/
http://blog.ponto-dot.com/2010/08/15/setting-up-common-lisp-on-a-web-server
http://blog.ponto-dot.com/2009/08/18/hunchentoot-behind-proxy-server
http://blog.ponto-dot.com/2009/08/18/slime-tramp-filename-translation
http://www.sbcl.org/
http://marijnhaverbeke.nl/postmodern/
http://weitz.de/hunchentoot/
http://www.quicklisp.org/
http://www.lisp.org
http://planet.lisp.org
http://gigamonkeys.com/book/

Advertisement

Tags: , , , , , , , , ,

26 Responses to “Lisp Web Server From Scratch using Hunchentoot and Nginx”

  1. archimag Says:

    I’m doing much, much easier:
    http://restas.lisper.ru/en/manual/daemon.html
    https://github.com/archimag/restas/blob/master/contrib/restas-daemon.lisp

  2. zaries Says:

    Kewl well I will give that a try some time, it does look simpler.

    The only question I have is, is *asdf-central-registry* not depreciated in ASDF2 or at least not the preferred method to use any more?

  3. archimag Says:

    I have not yet worked with ASDF2, but make the necessary corrections in restas-daemon.lisp can be very easy.

    The main thing is that I have used “pure lisp” daemon instead of complex solutions based on the screen or detachtty. I tried initially to use a screen and detachtty in production and was very unhappy, it is difficult and unstable. Incidentally, I wrote restas-daemon.lisp based on analysis source code of the detachtty.

  4. Andreas Says:

    Two additions. First, a smaller one: Cyrus Harmon’s hunchentoot-vhost allows virtual host dispatching without requiring nginx.

    Second, and this one is important, do not ever run Hunchentoot without considering what happens when suddenly slashdot links to you. Hunchentoot has no resource limits whatsoever, and I got burnt by that pretty badly. Always use a varnish cache in front of it.

  5. Robert Goldman Says:

    ASDF:*CENTRAL-REGISTRY* is nominally deprecated in ASDF2. But there are still lots of things I can do with ASDF:*CENTRAL-REGISTRY* that I can’t do without it. I don’t intend to give it up for a long time to come, and if it ain’t broke for you, I wouldn’t fix it.

  6. zaries Says:

    As far as I could see hunchentoot-vhost does not work with define-easy-handler. I had a look at doing my own virtual hosts but define-easy-handler has its own global dispatch table which prevents adding vhosts to hunchentoot in a natural way without changing hunchentoot.

    My sites are all SaaS web applications that have very low hit counts so I have not needed to protect against high traffic, but I will have a look at varnish. Thanx for the tip I am sure some of the other readers can use it too.

  7. Stephen Says:

    Forgive my ignorance, but what can this be used for in particular (ie what is its forte?)

  8. zaries Says:

    Stephen I would say the best use would be for sites that have dynamic content generated by lisp progams. I use it heavily for SaaS systems.

  9. Vladimir Sedach Says:

    Throwing Hunchentoot behind nginx you can do the same thing as with Varnish.

    But the problem should be solved at the root. Threads in CL implementations need to be made lighter-weight. Hunchentoot has provisions for plugging in a thread pool already (by default it launches a new thread per connection, which is quite expensive).

    It’s also possible to separate request processing from reading requests from the socket in such a way that the latter can be done in one thread with event-based I/O, so thread resources are not used up waiting on reading from slow connections. This will require major changes to Hunchentoot internals though.

    If you do both, Hunchentoot will be able to accept a very large number of connections, and reply to as many of them at the same time as the thread pool allows, which should hopefully coincide with the machine’s actual resources, so you can get maximum server utilization without thrashing when there’s a flood of connections.

  10. archimag Says:

    > Itโ€™s also possible to separate request processing from reading requests
    > from the socket

    I’m going to do just that as only a little relieved from the main job.

  11. Vladimir Sedach Says:

    Let me know if you need help. I would like to contribute to writing a “usocket-event” library to do this, rather than having Hunchentoot use iolib.

  12. gonzojive Says:

    Super post. I think we should hold a contest to see who can create the simplest tutorial for getting a web app up and running in Common Lisp.

    The ability to start/stop the web server is kind of a hassle. I wrote start-stop-lisp-daemon to collect the aforementioned script into a legitimate project (https://github.com/gonzojive/start-stop-lisp-daemon.git). restas-daemon.lisp looks interesting too–we should document the advantages and disadvantages of the pure lisp vs. detachtty/screen-based approach. I use screen a lot anyway, so it made sense for my own admin style to integrate screen into start-stop-lisp-daemon. Perhaps restas-daemon.lisp is just as good, though.

  13. zaries Says:

    I don’t know about a contest but I would support a project where this is packaged and expanded. Who knows we could end up with a simple install package, that could go out with linux distros. Any thing to make lisp more accessible is worth the work.

  14. Andreas Says:

    Indeed, I am not using define-easy-handler, but create-prefix-dispatcher and create-folder-dispatcher, which work just fine with hunchentoot-vhost.

    It might be possible to hack something that dynamically binds *easy-handler-alist* depending on the vhost selected, in order to make easy handlers work. But I haven’t researched this much. I think we should nudge Edi to include this functionality into hunchentoot at some time, together with resource limits.

    I hadn’t realized that nginx does caching too these days, it seems the functionality could be better documented. I’ve had good experiences with varnish, though, I get 7000 requests per second with 200 parallel connections out of my varnish/hunchentoot/nuclblog setup.

  15. zaries Says:

    I was looking at setting request-dispatcher on the acceptor to handle dispatch tables per domain.

    request-distpatcher = “A designator for the request dispatcher function used by this acceptor. A function which accepts a REQUEST object and calls a request handler of its choice (and returns its return value). The default is the unexported symbol LIST-REQUEST-DISPATCHER which works through the list *DISPATCH-TABLE*.”

    But that will no take care of easy handlers. If we can get Edi to change (or accept a change) that will get easy handlers to not use its own internal variable (*easy-handler-alist*) then we are there in my mind. From what I can see *easy-handler-alist* is only used by 2 functions (dispatch-easy-handlers and define-easy-handler) as a “shared resource” so it should be quick to change.

    What do you think?

  16. archimag Says:

    define-easy-handler – a very old-fashioned system. RESTAS provides modern routes-based dispatching system (http://restas.lisper.ru/en/manual/routes.html) and has full support for virtual hosts (http://restas.lisper.ru/en/ref/index.html#restas-start).

  17. zaries Says:

    I read the RESTAS docs but I fail to see how (restas:define-module #:articles) + (restas:define-route article (“articles”) …) is much better (less work or really more intuitive) from (define-easy-handler (article :uri “/domain-prefix/article.html”)…), especially taking in to consideration that with both methods you need to change code and/or recompile/reload to change a route.

    I am also not particularly fond of the route syntax like “/foo/:bar/:id/:page” I would have preferred lisp parameters, but that is just my personal preference.

    Generating the “link” urls would be beneficial to reach the goal of being able to move code and change urls easily. However I don’t think routes would be needed to get that right. Routes would just be one way of doing it.

    Having different functions that handle different actions for a page (in your terms the :requirement parameter) is nice. However once again I would go a different route. You can hook into hunchentoot natively (in a lispy clos way) to do process routing and processing before and after the request is served.

    All in all its personal preferences, but hey this is a blog so its allowed ๐Ÿ˜‰

  18. archimag Says:

    The main difference is that define-route is used to create “clean URLs” (http://en.wikipedia.org/wiki/Clean_URLs), which is not possible when using the define-easy-handler. In addition, define-module determines a modular components with the possibility of reuse (via mount-submodule).

    You are certainly free to use any approach ๐Ÿ™‚

  19. feite Says:

    First of all, I’d like to thank you for making this tutorial! I’ve been programming for fun in Common Lisp for quite a while now, but did mostly Project-Euler stuff, so this was my first “real thing”. I am a complete newbie to linux, making websites, and pretty much everything this tutorial covers. I would just like to point out a few errors/omissions/possible clarifications in your post, so that others won’t have to spend as much time as I did figuring it out! Some of these things might seem really obvious to experienced people, but sure weren’t to me.

    1. When installing SBCL, make sure you install the right version! I tried using this link, which seemed to work fine (installing gave no errors) however the program wouldn’t run. After a while I realized that I installed the 32-bit version but had a 64-bit version box running.. oops!

    2. There is a bug in SBCL 1.0.44 (the newest version) that can easily be fixed if you know what to do. The error message is something like “Not an absolute pathname #P”~/.clc/systems/”.”. I found the fix here: https://bugs.launchpad.net/ubuntu/+source/common-lisp-controller/+bug/661701.

    3. (require ‘swank) is all good and fun, however if you haven’t installed it it won’t work :). So don’t forget to (asdf-install:install ‘swank) as it isn’t included in hunchentoot or cl-who.

    4. In my-home-project.asd, (:file “master-page” ) should be changed to (:file “home-page”).

    5. Also in my-home-project.asd, a paren is missing, it should be
    (define-easy-handler (home-page :uri “/myhome/home.html”)
    () .. etc.

    6. As was pointed out in the tutorial, /var/lib/hunchentoot/log/hunchentoot.dribble is really the place to look for errors if you keep getting 502. It might also be a good idea to put the whole restart server thing into a script, saves you some time when testing. I therefore proudly present my first ever shell script that automates (ps aux, kill -pid) ;). The rest of the lines can of course just be copied and pasted.
    ps -ef | grep detachtty | grep -v grep | awk ‘{print$2}’ | xargs sudo kill

    Hopefully this saved some people some time, as it can be quite hard to find the solutions online.

  20. zaries Says:

    Thank you for the comments I made the fixes and added some of the tweaks you suggested. Just a note Quicklisp is there to simplify installing lisp libraries so (ql:quickload “swank”) is preferred to (asdf-install:install โ€˜swank).

  21. Roger Says:

    I had problems with the routing /myhome/ part, so I changed the start of hunchentoot from:

    (defparameter *httpd*
    (hunchentoot:start
    (make-instance ‘hunchentoot:acceptor
    :port *httpd-port*)))

    to

    (defparameter *httpd*
    (hunchentoot:start
    (make-instance ‘hunchentoot:easy-acceptor
    :port *httpd-port*)))

    and now everything works.

  22. zaries Says:

    Roger yes, that is because this article was written before hunchentoot 1.2.1. I wrote about the easy-acceptor change in https://zaries.wordpress.com/2011/11/15/hunchentoot-1-2-1-gotcha/.

    I will consider whether to just update this article or if I should write a new one since it has been a year since I wrote it.

  23. Lele Says:

    Wow, great howto! Thank you for sharing. Bookmarked.

    > Second, and this one is important, do not ever run Hunchentoot without considering what happens when suddenly slashdot links to you. Hunchentoot has no resource limits whatsoever, and I got burnt by that pretty badly. Always use a varnish cache in front of it.

    Doesn’t this setup address such issue by using Nginx? Well, as long connections access static pages, I suppose.

  24. zaries Says:

    Well how many pages these days are actually static?

    For curiosities sake I ran a simple siege against a production website of mine based on the configuration in this article.

    siege -c25 -t1M

    Lifting the server siege… done.
    Transactions: 1058 hits
    Availability: 100.00 %
    Elapsed time: 59.75 secs
    Data transferred: 4.47 MB
    Response time: 0.87 secs
    Transaction rate: 17.71 trans/sec
    Throughput: 0.07 MB/sec
    Concurrency: 15.41
    Successful transactions: 1058
    Failed transactions: 0
    Longest transaction: 4.06
    Shortest transaction: 0.66

    During this time I was keeping an eye on cpu usage and sbcl was using 20-25% and postgres was using about 4%.

    I think I will do some more testing and blog about it ๐Ÿ˜‰

  25. konoron Says:

    Thank you for sharing your experience.

    Why would you need the following IPTables rules for a web server? Thanks.

    # Allows SMTP access and SSMTP
    -A INPUT -p tcp –dport 25 -j ACCEPT
    -A INPUT -p tcp –dport 465 -j ACCEPT

    # Allows pop and pops connections
    -A INPUT -p tcp –dport 110 -j ACCEPT
    -A INPUT -p tcp –dport 995 -j ACCEPT

    # Allows imap and imaps connections
    -A INPUT -p tcp –dport 143 -j ACCEPT
    -A INPUT -p tcp –dport 993 -j ACCEPT

  26. zaries Says:

    You don’t, but most of my web apps have some mail interaction so I added it, and a lot of people that have a small virtual server out there don’t split them between webserver and mail server because of economics.

    Security is not my field of expertize, and the default set of ip rules given here could most likely be trimmed down even more. Any suggestions are welcome.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s


%d bloggers like this: