Fresh Blurbs

Install Node.js and Express.js with Nginx on Debian Lenny

Following instructions were tested on a RackSpace Cloud Server. It should work for any other reasonably configured, mostly bare-bones Debian Lenny and latest Ubuntu (e.g. on other popular hosting options like: SliceHost, Linode etc.). First, let's install some essentials:

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install build-essential
$ gcc -v
$ make -v

$ sudo apt-get install python-software-properties
$ sudo apt-get install libssl-dev libreadline-dev
$ sudo apt-get install git-core curl

Now we can proceed with Node.js installation. There are two ways to install Node.js, directly (not recommended due to maintenance cost) or with a version manager like nvm (highly recommended in most cases):

Method 1: Installing Node.js with NVM

Attention: this is the recommended way.

$ git clone git://github.com/creationix/nvm.git ~/.nvm
$ . ~/.nvm/nvm.sh
$ nvm ls
$ nvm install v0.8.12
$ nvm alias default v0.8.12
$ nvm ls
$ nvm help

Please make sure to add following line to your (and by "your" we mean the user which node apps will be executed as) ~/.bashrc or ~/.profile:

. ~/.nvm/nvm.sh

You may want to log out and log back in, to test that login scripts work.

Method 2: Compiling Node.js from sources yourself

Attention: this is NOT recommended. You should use NVM or Nave instead.

$ cd /usr/local/src/
$ sudo wget http://nodejs.org/dist/node-v0.8.12.tar.gz
$ sudo tar xzvf node-v0.8.12.tar.gz 
$ cd node-v0.8.12
$ sudo ./configure
$ sudo make
$ sudo make install
$ whereis node
$ node -v

Installing NPM Package Manager

You do not need this if you installed Node using NVM, because NVM installs NPM as well. Let's install Node's package manager (make sure your non-root user is also part of the "webmaster" group)::

$ cd
$ curl http://npmjs.org/install.sh | sh
$ which npm

Next, you can run "npm search" (takes a while) to see the abundance of Node.js packages available or "npm ls" to see packages already installed.

Another Node Version Manager: Nave

Nave is different from NVM in that it does not require sourcing a shells script. Comparing Nave and NVM, Nave's author wrote: "Nvm is also really nice, but has to be sourced rather than being run, and thus is a little bit wonky for some use cases. But it doesn't involve subshells, which makes it better for many others." I think they are both pretty great, so it's a matter of taste, maybe. Nave allows us to run multiple versions of node in parallel and switch between them, similar to how rvm and virtualenv do it for Ruby and Python respectively:

$ cd /usr/local/src/npm/
$ sudo npm install -g nave
$ nave -v

First Express App

Let's create our first Express app now

$ sudo mkdir  /opt/apps
$ sudo chmod -R 775 /opt/apps/
$ sudo chown ${USER}:webmaster -R /opt/apps/
$ mkdir /opt/apps/firstapp

Let's install Express.js web framework for Node.js under our project:

$ cd /opt/apps/firstapp
$ npm install express

create /opt/apps/firstapp/app.js with the following source code:

/* Module dependencies. */
var express = require('express');

var app = express.createServer();

app.get('/', function(req, res){
  res.send('Hello World');
});

app.listen(3000);

and save. You can start your Node/Express.js app from shell with:

$ node /opt/apps/firstapp/app.js

After which, if you go to http://yourdomain.com:3000/ you should see a nice "Hello World" message. Voila! We made our first Node.js app! Of course, in the real world you will probably want to put a web-server in front of the node.js app. If for no other reason: to serve static content faster and to securely attach to port 80. Since the big deal with node.js is its non-blocking architecture, it makes sense to front-end it with a non-blocking web-server as well. Let's see how we can route our node.js app through Nginx.

Installing Nginx

Disclaimer: instructions adapted from a SliceHost Tutorial

$ sudo apt-get install build-essential
$ sudo apt-get install libc6 libpcre3 libpcre3-dev libpcrecpp0 libssl0.9.8 libssl-dev zlib1g zlib1g-dev lsb-base
$ cd /usr/local/src
$ cd /usr/local/src/
$ sudo wget http://nginx.org/download/nginx-1.3.7.tar.gz
$ sudo tar xzvf nginx-1.3.7.tar.gz 
$ cd nginx-1.3.7
$ sudo ./configure --sbin-path=/usr/local/sbin --with-http_ssl_module
$ sudo make && sudo make install
$ sudo ln -s /usr/local/nginx/conf /etc/nginx

Configure Nginx

Edit file: /etc/nginx/nginx.conf and insert the following line at the end of the "http" section (typically: before the very last closing curly brace "}" in the file, unless Nginx changes the default config file):

include sites-enabled/*;

Create folder /etc/nginx/sites-enabled/ and edit /etc/nginx/sites-enabled/firstapp.conf file with content similar to:

server {
  listen 80;
  server_name yourdomain.com;

  # Let's put all static files like images, js and css in sub-folder: public
  root /opt/apps/firstapp/public;

  #  static content
  location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
    # access_log        off;
    expires           15d;
  }

  location / {
    proxy_pass         http://127.0.0.1:3000;
    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_redirect off;
  }

  gzip on;
  gzip_comp_level 2;
  gzip_proxied any;
  gzip_min_length  1000;
  gzip_disable     "MSIE [1-6]\."
  gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
}

Attention: If you use some CSS shorthand frameworks, as Will explains in his comment you will want to remove css files from the list of static content that you'd like NginX to handle for you. You can start nginx with:

$ sudo /usr/local/sbin/nginx

If you later need to edit Nginx configuration and reload it you can run:

$ /usr/local/sbin/nginx -s reload

You can test Nginx configuration for errors with:

$ sudo /usr/local/sbin/nginx -t

Once Nginx is properly configured, you should be able to see the same express.js response page with http://yourdomain.com (without the port 3000 and assuming that you are still running the node.js application). At that point request is routed through Nginx and we have also added instructions to serve static content (images, css, javascript etc.) directly from Nginx (subfolder "public"), bypassing Node.js

Bundling Dependencies

Instead of manually installing required modules, you can define dependencies of your app declaratively and let npm manage it for you. If you are familiar with Ruby On Rails, this is a process similar to using Gemfiles and Bundler. First, you need to create a package.json in the same folder where your app.js lives:

{
  "author": "Node Noob"
, "dependencies": {
    "express": ">= 2.4"
  , "expresso": ""
  , "jade": "0.15.13"
  , "should": ""
  }
, "description": "First Steps With Node.js"
, "main": "./app.js"
, "name": "app"
, "version": "1.0"
}    

after which you can run either:

$ npm bundle 

or in newer versions of npm, just:

$ npm install

to get all the required dependencies installed. see: $npm help json for more details about the format of the package file.

Startup and Management

One last step is to create and configure Nginx startup scripts, so that if the server restarts, Nginx comes back up automatically. Slicehost has a great article for the detailed steps of achieving just that, so you should refer to it: http://articles.slicehost.com/2009/8/3/debian-lenny-adding-an-nginx-init-script For daemonizing Node process itself, you may want to review this code snippet: https://gist.github.com/1045395, but you are probably much better off using Upstart (especially on Debian Squeeze which comes pre-installed with one). For more information read Tim Smart's blog post: http://howtonode.org/deploying-node-upstart-monit

Multi-Process Node.js

Node.js is a single-threaded system. Yes, you heard it right. There is only one thread. Due to its event-loop-based asynchronous and non-blocking nature it scales great even as a single thread, but if you are running on a multi-core CPU you should want to start multiple Node.js processes so you can leverage available cores. To utilize multiple Node processes you should use Clustering, which is now part of Node.js core.

Next Steps

To understand clustering, hot reloading and take a look at a sample Node/Express.js project, dive into the code of NodeBootstrap on Github.

comments powered by Disqus