Updating a Node.js web application without downtime

I have a Node.js/Express web application running on localhost:3010 that's on, say, version 1.2.2. Suppose I want to update the web application to version 1.3.0 without any downtime. What's the best way?

Using NGINX in front of the Node.js web application

Fortunately NGINX sits in front of my web application. Incoming HTTP requests are sent first to NGINX, and then NGINX forwards the requests to my Node.js/Express web application. Here's what my configuration file looks like from /etc/nginx/sites-available/www.arepeopletalkingaboutit.com

server {
  server_name www.arepeopletalkingabout.com;
  listen 80;
  listen [::]:80;

  root /home/arp/apps/arepeopletalkingaboutit-3010/public;

 location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:3010;
 }
}

There are two things worth noting. The root directory and the port. In this situation, my Node.js application is hanging out at /home/arp/apps/arepeopletalkingaboutit-3010/ and is being served on port 3010. In my Node.js/Express application's bin/www I have:

var port = normalizePort(process.env.PORT || '3010');

Spin up a second Node.js web application

I'll actually start up a second Node.js web application with the new version in a new directory and on a new port. Your process may look different than mine, but mine looked something like this:

$ git clone git@mygitlab.com/arepeopletalkingaboutit.git arepeopletalkingaboutit-3011
$ cd arepeopletalkingaboutit-3011
$ npm install
(update bin/www to use new port)
$ export NODE_ENV=production
$ forever start --uid "1.3.0:3011" bin/www

In the above example, I just give the process a unique identifier of "1.3.0:3011" so I could differentiate between other processes.

When I'm done, I'll have two Node.js/Express web applications at these locations:

/home/arp/apps/arepeopletalkingaboutit-3010 (on version 1.2.2)
/home/arp/apps/arepeopletalkingaboutit-3011 (on version 1.3.0)

And in /home/arp/apps/arepeopletalkingaboutit-3010/bin/www

var port = normalizePort(process.env.PORT || '3010');

And in /home/arp/apps/arepeopletalkingaboutit-3011/bin/www

var port = normalizePort(process.env.PORT || '3011');

At this point, both Node.js/Express web applications are running, but only one is reachable from the world wide web due to our configuration of NGINX:

A hot reload of NGINX

NGINX has a neat feature where you can reload its settings without any downtime. This means we can change the settings in /etc/nginx/sites-available/www.arepeopletalkingaboutit.com and save the file, but the changes won't go into effect until we reload or restart NGINX.

I'm going to take advantage of that by changing the file:

server {
  server_name www.arepeopletalkingaboutit.com;
  listen 80;
  listen [::]:80;

  root /home/arp/apps/arepeopletalkingaboutit-3011/public;

  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:3011;
  }
}

And then execute a service nginx reload.

NGINX should now be forwarding requests to the new web application:

Verifying the changes

I always like to do a sanity check to make sure this is working. The easiest is to make sure I can still go to the web application at www.arepeopletalkingaboutit.com. I'll keep both logs open by using the tail command and see which web application handles the request.

I really like forever to keep my Node.js / Express web applications alive and running, so check that out if you're curious.