As I finished my first real Node.JS app last week, I was looking for a nice way to deploy the application easily to my server. I found a very long articleusing upstart and Spark, which I thought is way to complicated. I went on for another way and finally stopped at a project called nDistro, which is less a deployment tool but a handy one for getting dependencies work.
Before diving into the deployment stuff, let’s create an example application printing Hello World in the browser:
[~/Projects] express HelloWorld
create : HelloWorld
create : HelloWorld/app.js
create : HelloWorld/pids
create : HelloWorld/views/partials
create : HelloWorld/views/layout.jade
create : HelloWorld/views/index.jade
create : HelloWorld/public/stylesheets/style.less
create : HelloWorld/logs
create : HelloWorld/public/images
create : HelloWorld/test
create : HelloWorld/test/app.test.js
[~/Projects] cd HelloWorld
[~/Projects/HelloWorld] pico views/index.jade
#paste in the following
h1 Deploying a Node.JS application with Express and nDistro
p Hello World!
[~/Projects/HelloWorld] node app.js
Having all needed libraries installed, you can just open your browser and visit http://localhost:3000! If you haven’t all the libraries… read on and get it running!
With nDistro you can easily manage library dependencies, writing a script containing used libraries. To install nDistro, just do:
cd /usr/local/bin && curl http://github.com/visionmedia/ndistro/raw/master/install | sh
You should now be able to run the ndistro command in your console:
Error: .ndistro not found in this directory
To install all the dependencies you could use npm, which is great for developing software locally, but which I would like to avoid in production.You probably ask why to avoid npm. Well, I like to seperate dependencies from the environment as much as I can. Dealing with nDistro is doing exactly that.
To prepare your project for nDistro, add the following line at the beginning of app.js:
require.paths.unshift(__dirname + “/lib/node”)
This will force Node.JS to look for libraries in that specific folder. Running the app will fail with the following error:
[~/Projects/HelloWorld] node app.js
throw new Error(“Cannot find module ‘” + request + “’”);
Error: Cannot find module ‘express’
at loadModule (node.js:275:15)
at require (node.js:411:14)
at Object. (/Users/prototype/Projects/HelloWorld/app.js:2:1)
at Module._compile (node.js:461:23)
at Module._loadScriptSync (node.js:468:10)
at Module.loadSync (node.js:338:12)
at Object.runMain (node.js:521:24)
Now open the file .ndistro and add this:
module visionmedia express 1.0.0beta2
module visionmedia jade
Run ndistro in the app folder and watch the output:
… installing express 1.0.0beta2
… installing bin/express
… installing jade
… installation complete
As you will see, nDistro created a modules folder, which contains the source code of all specified dependencies. Furthermore there will be a folder called lib/node, which has links to the sources and which can be required using the require command. What you actually wrote in that .ndistro file are definitions of github repositories. The syntax is:
module user project
Running the example app again should work now!
Note: While developing your application make sure to write dependencies to your .ndistro file!
Why I really like nDistro a lot is the fact that it can also generate a fully working node binary. That mean you don’t have to setup your environment including downloading and compiling node and it’s dependencies. Just add the following to your .ndistro:
Important note: There are currently not all releases available, but AFAIK it should be in the future. Which versions of node are supported, can be seen here: http://github.com/visionmedia/nodes
Running ndistro again, will add a node binary to the bin folder. So you can now just run ./bin/node app.js. Awesome!
Starting the application this way, will make node look automatically into the project’s lib folder, when calling the require method. So the inserted line (require.paths.unshift …) is now obsolete and can be dropped.
I use git for managing my source code, but using svn or any other way will work either. At this time I assume,that your project is under source control or that you can just get your code onto your server.
Sooo… connect to your server via SSH as root and execute the following lines. BTW: I have an Ubuntu server so thecommands might differ for other operating systems.
sudo useradd -d /srv/www/<projectUrl> <projectUrl>
sudo passwd <projectUrl>
chown <projectUrl>:<projectUrl> ./<projectUrl>
This will create a new user named like the url, the project will be available through. You can of course name it as you want to, but I am doing it that way.
To setup apache do that:
ProxyPass / http://localhost:\<port>/
ProxyPassReverse / http://localhost:\<port>/
The port is just the one your application listens on. The example app will listen on port 3000.
Now that the server is prepared, we can go on and get the application running. Login with your newly created user:
# somehow checkout your code
# e.g. git clone email@example.com:<projectUrl>
mv <projectUrl>/* .
rm -r <projectUrl>
This will download the project from your repository, moves the content of the repository folder into the current one, install all dependencies via ndistro and finally start the application. You should now be able to access your app via the specific project url.
You should now test all of the applications functions to be sure, you fetched all dependencies in your .ndistro file. If the app crashes, take a look at the console and fix that issue. If a library is missing, just add it to .ndistro and commit it to source control once you finished. When everything works the way it should, start your application the following way:
EXPRESS_ENV=production ./bin/node app.js > ./logs/production.log &
This will start the server in production mode and send all logging to logs/production.log. The & at the end will send the server to the background, so that you can just go on using your console. If you want to check the log, use the tail command:
tail -f logs/production.log
Note: If you stop the node server and restart it after some changes, you maybe have to restart the apache as well, due to a still visible “Service not available” notification. Thanks for that note goes to Robert :)
I highly recommend you to register for a monitoring service and let your application be monitored. I’m using UptimeRobot which is free and will notify you via E-Mail, SMS or Twitter once your server isbroken.
I will take a look at upstart and Spark to checkout if this can be useful somehow.
Let me know if you got stuck or what could be explained better :)