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!