25 May 2010

Deploying node.js with Upstart

Upstart seems like a really cool project to replace the old init.d scripts with an event-based system. However, I've found working with it to be pretty frustrating. Events that I expected to fire don't, and its difficult to debug. After a bit of digging around and plenty of trial and error I've managed to come up with a script that works on Lucid (and Karmic with a few tweaks). So, for my own reference, and in the hope that I save some lucky Googler the time I spent investigating, here is my process for deploying a node.js application with upstart:

If you don't have one already, I recommend creating a user to run your applications as. The following command will create a user called 'node':

sudo adduser \
        --system \
        --shell /bin/bash \
        --gecos 'user for running node.js projects' \
        --group \
        --disabled-password \
        --home /home/node \
        node

We can then create an upstart script to start the node.js server. Upstart scripts go in /etc/init on Ubuntu. Create the file /etc/init/myserver.conf (replacing myserver with a more sensible name for your application), and add the following:

description "my server"
author  "caolan"

start on (local-filesystems and net-device-up IFACE=eth0)
stop on shutdown

respawn

exec sudo -u node sh -c "/usr/local/bin/node /var/local/sites/myserver/start.js >> /var/log/myserver.log 2>&1"

The start on line tells upstart to run the script when local filesystems are available and the network is up (you may want to change eth0 to a different device if you're crazy enough to run a server over wireless).

If you're using Karmic, the above start on line did not work in my tests. I was able to get the script running by using: start on runlevel [2345] instead.

Also, note that Ubuntu boots without waiting for /home to be mounted, so I don't advise storing your application there, put it somewhere in /opt or /var instead. It should be possible to force Ubuntu to wait for /home to become available by adding the bootwait option to your /home partition in /etc/fstab. However, I don't recommend this as it has been associated with bugs in Karmic which may stop the computer from booting. Adding this has never worked for me in Lucid either, perhaps because of an encrypted /home partition. Thankfully /var/local/sites is a much more sensible to put your applications anyway ;)

I've seen a number of examples showing this line as:

start on startup

If you are not on Ubuntu, or are on a newer version than Lucid you might wish to try this. However, I have never managed to get this event to fire.

The respawn line tells upstart to run this script again should the process exit unexpectedly and the final line runs the application (in this example its start.js). The application is run as the user 'node' and logs stdout and stderr to /var/log/myserver.log

Next, I checkout/clone/copy my application to /var/local/sites/myserver:

sudo mkdir -p /var/local/sites/myserver
sudo chown node /var/local/sites/myserver
cd /var/local/sites/myserver
sudo -u node git clone /path/to/myserver_repository.git .

Then, make sure the log files have the correct permissions:

sudo touch /var/log/myserver.log
sudo chown node /var/log/myserver.log

We're now ready to test the upstart script:

$ sudo start myserver
myserver start/running, process 1234

$ sudo status myserver
myserver start/running, process 1234

The second command ensures that the server didn't immediately die after starting. If you're seeing output similar to the above, you can now try acessing your server to see if its running ok:

$ curl localhost:8000

If that all looks sensible, lets try stopping the server:

$ sudo stop myserver
myserver stop/waiting

If you've got this far then well done! Unfortunately, the most-likely-to-break part is yet to come… getting the script to run on startup! Try restarting and see if your application is running once the machine is back up. If you have any problems, take a look at my advice above regarding the start on line and where to store your application files. Failing that, you should be able to take a look at the logs for your application at /var/log/myserver.log.

Once you've got a template that works, deploying with upstart is a breeze. Good luck and happy hacking!

Note: I'm not a sysadmin, if you think you know better then you probably do! Make some noise in the comments.