Deploy Resilient Node.js Apps to Ubuntu with Forever and Nginx

Apr 15, 2016

You're finally done working on your shiny new Node.js app and now it's time to deploy it to production!

However, what may seem like a trivial task soon becomes a quite complicated one:

  • Your app cannot bind to ports 80 / 443 (or any other port number that is < 1024, for that matter) on linux without you granting root privileges to your app (which is considered dangerous)
  • Your app may crash on an unhandled exception and will become unavailable until you manually restart it
  • Your server will reboot eventually, so you'll need to set up a mechanism to restart your Node.js app when the server reboots

Luckily, you're not the first person in the world deploying Node.js to production, and these challenges are quite easy to overcome with the right tools and mindset.

Let's get to it!

The following guide is custom-tailored for Ubuntu Server 14.04 LTS.

Create a System User

Let's go ahead and create a regular system user called node that will run your Node.js app. This is necessary in order to avoid making it possible for an attacker to execute arbitrary code as the ubuntu account (which has root privileges) in case some kind of remote code execution vulnerability is found in your application.

sudo useradd --system --create-home node

This command will also create a home directory for the node user in /home/node.

Install Node.js

There are several popular ways to install Node.js on Ubuntu, the simplest (and most recommended) seems to be via the NodeSource PPA.

curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
sudo apt-get install -y nodejs

Need to install an alternative Node.js version? Check out the official NodeSource installation instructions.

Transfer Your App

The next step involves transferring your Node.js source code to the Ubuntu machine. How you go about this depends on your source control system, personal preference, and any corporate security restrictions you might have to adhere to. But the end goal here is to get your source code up on the server so you can run it.

If your code is hosted in a remote git repository, install git:

sudo apt-get install git

Switch the shell to the node user and head over to its home directory:

sudo su node
cd ~

Create a folder called app within the node user's home folder, /home/node:

mkdir app

Clone your Node.js app to the machine and into the app folder you just created:

git clone https://github.com/your/awesome-project.git app

Prepare for Deployment

Let's install your app's dependencies. This one's rather easy -- simply running npm install should be sufficient, as long as you set up your package.json correctly and didn't omit any required dependencies:

cd app
npm install

Next, make sure that if your Node.js app will listen for HTTP requests, it is configured to listen on a port number that is greater than 1024. A few popular choices are 1337, 3000 or 8080. Otherwise, you won't be able to run your app without root privileges, due to the linux security restriction described above.

Install Forever

Now it's time to make sure that we set up a mechanism to keep your Node.js app running forever, even if it crashes, but it won't, since you're such an awesome programmer, right?

Wrong. Your app will crash, and it won't always be your fault. One of the packages you depend on might fail miserably and you'll be left with an unresponsive server and angry clients.

Now that we agree that the inevitable will occur, let's put a protective mechanism in place. Forever is a simple CLI tool built for ensuring that a given Node.js script runs continuously (i.e. forever). Installing it is dead simple via npm, so switch back out into the ubuntu user's shell (Ctrl + A + D) and run the following command:

sudo npm install -g forever

Install Nginx

Next, we'll install nginx, a high performance, open source web server (similar to Apache) that is widely-used as a reverse proxy for Node.js apps.

The idea here is that nginx will listen on port 80 on behalf of your app and forward all incoming requests directly to your app on port 8080, for example.

Check out my Binding a Node.js App to Port 80 with Nginx tutorial for complete step-by-step instructions.

Running Your App

Now that everything's installed and configured, let's make sure your app actually works!

Start your app with forever (make sure to switch over to the node user first):

sudo su node
cd ~/app
forever start server.js

Replace server.js with the name of the file that is your app's main entry point.

Finally, verify that your app is running by connecting to it via http://1.2.3.4/. You may need to configure your server's firewall to allow incoming connections on port 80.

Survive Reboots

We're not done yet! One more thing. Let's make sure that if the server reboots, your app will automatically start itself when the server boots up.

This is relatively easy to achieve via a @reboot entry in the node user's crontab.

The linux crontab is a simple text file with a list of commands meant to be run at specified times.

Let's issue the following command to edit the node user's crontab file:

crontab -e

Next, let's add an entry to the bottom of this file:

@reboot forever start /home/node/app/server.js

Again, replace server.js with the name of the file that is your app's main entry point.

This tells crontab to run the specified forever start command after the system reboots.

Finally, save the file (Ctrl + O if you're using nano). Only one thing left to do -- make sure that your app will initialize itself successfully after server reboot.

Switch back to the ubuntu user (Ctrl + A + D) and run the following command:

sudo reboot

Grab a cup of joe as the server reboots. Once it's back up, attempt to access your Node.js app. If everything works as expected, great job, you've just deployed a resilient Node.js app to production!