Step One: Put Your Node in a Box

In my last post, I extolled the virtues of isolating repeatable, versionable development environments in VirtualBox virtual machines with Vagrant. I find this to be particularly important in a rapidly changing software ecosystem like that of Node.js. Node has quickly advanced to the latest 0.4 branch, and the API (though now ‘stable’) has been something of a moving target. Combine this with the relatively poor state of documentation for third party Node libraries, and it is quite easy to get into version hell on a development machine.

Introducing NodeBox

NodeBox uses a set of Chef cookbooks and a custom Vagrant configuration to automatically create VMs for Node application development. The NodeBox repository is intended to be an independent submodule of an application, and I put together a basic “Hello World” example app to demonstrate.

Here’s all it takes to get started with NodeBox (This requires Vagrant and its dependencies. See here. NodeBox requires Vagrant 0.7 or higher and VirtualBox 0.4.x):

$ git clone git://github.com/repeatingbeats/nodebox-example.git --recursive
$ cd nodebox-example/nodebox
$ vagrant up

After Vagrant takes a few minutes to do its magic, point your browser at http://localhost:8000 to see the application in action. The Node app serving this response can be found in app.js in the toplevel nodebox-example directory:

var fs = require('fs'),
    http = require('http')
;

var config = JSON.parse(fs.readFileSync(__dirname + '/nodebox.json'));
var env = config.environment;
var port = config.app_port || (env == "production" ? 8080: 80);

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World, I Am A NodeBox\n');
}).listen(port);
console.log("Server listening on port: " + port);

This simple application reads configuration variables out of nodebox.json and responds to all requests with the Hello World string.

Isn’t It Easier to Just Build Node and Get On With It?

To get started? Yes. But my goal here isn’t to build the most complex Hello World contraption imaginable. I’m trying to make it easy to mix and match Node versions, Node modules, server configurations, and eventually databases. I’m trying to make it easy to do this in a way that isolates each environment so that when I find one that works, I have a setup that’s ready to roll into production. I don’t want or need eighteen kajillion competing libraries on my laptop. NodeBox is a tool that lets me easily spin up a new environment, and I can blow it up whenever I feel like it.

NodeBox Configuration

NodeBox’s configuration can be adjusted using a required nodebox.json in the application’s root directory. Here’s the configuration for nodebox-example:

{
  "app_name": "nodebox-example",
  "environment": "production",
  "app_port": 8080,
  "host_port": 8000,
  "modules": [ "express",
               "nodeunit",
               "mongoose" ],
  "node_version": "0.4.0",
  "npm_version": "0.3.0-9"
}

Most of the options are self-explanatory. app_port is the port on the VM that Node listens on. This should be set to 80 if the environment is “development”. host_port is the port on on the host machine that forwards to port 80 on the VM. modules is an array of node modules that will be installed by NPM on the VM.

Production-ish NodeBox

NodeBox has recipes for both Development and Production server configurations. The Production recipe comes with the caveat that it is a work in progress, but it contains some basic extra features that make more sense on a production server than on a development server. NodeBox defaults to Production, mostly because the Production box takes fewer steps to get from git clone to Hello World.

The Production recipe hides Node behind the Nginx web server, which proxies requests to Node on an upstream port. Additionally, upstart is used to daemonize the Node process and allow easy starting and stopping of the app. The recipe starts the app during the initial chef configuration, but if you need to restart you can do this:

$ cd /path/to/nodebox-example/nodebox
$ vagrant ssh
$ sudo stop nodebox-example
$ sudo start nodebox-example

The Production NodeBox also uses Monit to restart the application when it dies. There are certainly plenty of other options for monitoring Node processes. I borrowed mine from here, with some slight modifications and of course the Chef templating.

Development NodeBox

Getting started with the development NodeBox requires an extra couple steps:

$ git clone git://github.com/repeatingbeats/nodebox-example.git --recursive
$ cd nodebox-example

You’ll need to modify nodebox.json slightly:

{
  ...
  "environment": "development",
  "app_port": 80,
  ...
}

Next:

$ cd nodebox
$ vagrant up
$ vagrant ssh

Finally, now that you’re in a shell on the VM:

$ cd /var/www/nodebox-example
$ sudo supervisor -p app.js -w app.js

Sudo is required so that Node can listen on port 80. Do this in the safety of your own VM! Back on the host machine, point your browser at http://localhost:8000 once more. You should see the Hello World message. Now, change something in app.js. Node-supervisor will detect the change, crash the app, and restart it. The next time you go to http://localhost:8000, you’ll be able to see your new changes.

Yes, the Development NodeBox takes a few more steps to get started, but for good reason. Starting Node from a shell on the VM lets you see your errors!

What Now?

I’ve just walked you through how to get started with a basic Node application using NodeBox. Presumably, if you actually tried this, you might want to start building something. Feel free to poke through the nodebox-example and nodebox repositories to see what’s going on under the hood.

The next step for NodeBox is going to be integration of persistence mechanisms. I’m envisioning an option in nodebox.json that will tell Chef to do all the necessary setup to get one or more of Redis, MongoDB, MySQL, etc. up and running on the VM. Once there are persistence options, the production NodeBox will have everything it needs to start bootstrapping real deployments.

If you’d like to be the very first to hear about future NodeBox developments intermixed with other inconsequential 140-character outbursts, you’ll probably want to follow me on Twitter.