Load Balancing Node.js Application Servers with NGINX and NGINX Plus

This deployment guide explains how to use NGINX and NGINX Plus to load balance HTTP and HTTPS traffic across a pool of Node.js® application servers. The detailed instructions in this guide apply to both cloud‑based and on‑premises deployments of Node.js.

About NGINX and NGINX Plus

NGINX is an open source web server and reverse proxy that is becoming increasingly popular because of its scalability, outstanding performance, and small footprint. NGINX was first created to solve the C10K problem (serving 10,000 simultaneous connections on a single web server). NGINX’s features and performance have made it a staple of high performance sites – powering more than 1 in 3 of the world’s million busiest web properties.

NGINX Plus is the commercially supported version of the open source NGINX software. NGINX Plus is a complete application delivery platform, extending the power of NGINX with a host of enterprise-ready capabilities that enhance a Node.js deployment and are instrumental to building web applications at scale:

About Node.js

Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event‑driven, non‑blocking I/O model that makes it lightweight and efficient. The package ecosystem for Node.js, npm, is the largest ecosystem of open source libraries in the world.

To download the Node.js software and get installation instructions, visit the Node.js website.

The information in this deployment guide applies equally to open source Node.js software and commercially supported Node.js frameworks.

Prerequisites and System Requirements

A Node.js application server installed and configured on a physical or virtual system.

A Linux system to host NGINX or NGINX Plus. To avoid potential conflicts with other applications, we recommend you install NGINX Plus on a fresh physical or virtual system. For the list of Linux distributions supported by NGINX Plus, see NGINX Plus Technical Specifications.

NGINX or NGINX Plus installed on the physical or virtual system. Some features are available only with NGINX Plus, including sophisticated session persistence, application health checks, live activity monitoring, and on‑the‑fly reconfiguration of upstream groups. For installation instructions for both products, see the NGINX Plus Admin Guide.

The instructions assume you have basic Linux system administration skills, including the following. Full instructions are not provided for these tasks.

Configuring and deploying a Node.js application

Installing Linux software from vendor‑supplied packages

Editing configuration files

Copying files between a central administrative system and Linux servers

Running basic commands to start and stop services

Reading log files

About Sample Values and Copying of Text

example.com is used as a sample domain name (in key names and configuration blocks). Replace it with your organization’s name.

Many NGINX and NGINX Plus configuration blocks in this guide list two sample Node.js application servers with IP addresses 192.168.33.11 and 192.168.33.12. Replace these addresses with the IP addresses of your Node.js servers. Include a line in the configuration block for each server if you have more or fewer than two.

For readability reasons, some commands appear on multiple lines. If you want to copy and paste them into a terminal window, we recommend that you first copy them into a text editor, where you can substitute the object names that are appropriate for your deployment and remove any extraneous formatting characters that your browser might insert.

The configuration examples in the step‑by‑step instructions include hyperlinks to the NGINX reference documentation, for easy access to more information about the directives. (If a directive appears multiple times in a section, only the first occurrence is hyperlinked.) We recommend that you do not copy hyperlinked text (or any other text) from this guide into your configuration files, because it might include unwanted link text and might not include whitespace and other formatting that makes the configuration easy to read. For more information, see Creating and Modifying Configuration Files.

Configuring an SSL/TLS Certificate for Client Traffic

If you plan to enable SSL/TLS encryption of traffic between NGINX or NGINX Plus and clients of your Node.js application, you need to configure a server certificate for NGINX or NGINX Plus.

If you are compiling NGINX from source, include the --with-http_ssl_module parameter to enable SSL/TLS support for HTTP traffic (the corresponding parameter for TCP/UDP is --with-stream_ssl_module, and for email is --with-;mail_ssl_module, but this guide does not cover those protocol types).

If using binaries from other providers, consult the provider documentation to determine if they support SSL/TLS.

There are several ways to obtain a server certificate, including the following. For your convenience, step‑by‑step instructions are provided for the second and third options.

If you already have an SSL certificate for NGINX or NGINX Plus installed on another UNIX or Linux system (including systems running Apache HTTP Server), copy it to the /etc/nginx/ssl directory on the NGINX or NGINX Plus server.

Generate a self‑signed certificate as described in Generating a Self‑Signed Certificate below. This is sufficient for testing scenarios, but clients of production deployments generally require a certificate signed by a certificate authority (CA).

Generating a Self‑Signed Certificate

Generate a public‑private key pair and a self‑signed server certificate in PEM format that is based on them.

Log in as the root user on a machine that has the openssl software installed.

Generate the key pair in PEM format (the default). To encrypt the private key, include the -des3 parameter. (Other encryption algorithms are available, listed on the man page for the genrsa command.) You are prompted for the passphrase used as the basis for encryption.

Create a backup of the key file in a secure location. If you lose the key, the certificate becomes unusable.

root# cp ~/private-key.pem secure-dir/private-key.pem.backup

Generate the certificate. Include the -new and -x509 parameters to make a new self‑signed certificate. Optionally include the -days parameter to change the key’s validity lifetime from the default of 30 days (10950 days is about 30 years). Respond to the prompts with values appropriate for your testing deployment.

Request a certificate from a CA or your internal security group, providing the CSR file (example.com.csr). As a reminder, never share private keys (.key files) directly with third parties.

The certificate needs to be PEM format rather than in the Windows‑compatible PFX format. If you request the certificate from a CA website yourself, choose NGINX or Apache (if available) when asked to select the server platform for which to generate the certificate.

Copy or move the certificate file and associated key files to the /etc/nginx/ssl directory on the NGINX Plus server.

Creating and Modifying Configuration Files

To reduce errors, this guide has you copy directives from files provided by NGINX, Inc. into your configuration files, instead of using a text editor to type in the directives yourself. Then you go through the sections in this guide (starting with Configuring Virtual Servers for HTTP and HTTPS Traffic) to learn how to modify the directives as required for your deployment.

As provided, there is one file for basic load balancing (with NGINX or NGINX Plus) and one file for enhanced load balancing (with NGINX Plus). If you are installing and configuring NGINX or NGINX Plus on a fresh Linux system and using it only to load balance Node.js traffic, you can use the provided file as your main configuration file, which by convention is called /etc/nginx/nginx.conf.

We recommend, however, that instead of a single configuration file you use the scheme that is set up automatically when you install an NGINX Plus package, especially if you already have an existing NGINX or NGINX Plus deployment or plan to expand your use of NGINX or NGINX Plus to other purposes in future. In the conventional scheme, the main configuration file is still called /etc/nginx/nginx.conf, but instead of including all directives in it, you create separate configuration files for different functions and store the files in the /etc/nginx/conf.d directory. You then use the include directive in the appropriate contexts of the main file to read in the contents of the function‑specific files.

If you have just installed NGINX or NGINX Plus there is a default configuration file, default.conf, in the /etc/nginx/conf.d directory. This configuration defined there is not appropriate for the deployment described in this guide, but you want to leave a file with that name in the directory so it does not get replaced with a new version the next time you upgrade NGINX or NGINX Plus. To save a copy for future reference you can copy it to a new name without the .conf extension.

(You can also access the URL in a browser and and copy the text into the indicated file.)

Note: If you download both files, place only one of them in the /etc/nginx/conf.d directory.

To set up the conventional configuration scheme, add an http configuration block in the main nginx.conf file, if it does not already exist. (The standard placement is below any global directives.) Add this include directive with the appropriate filename:

You can also use wildcard notation to reference all files that pertain to a certain function or traffic type in the appropriate context block. For example, if you name all HTTP configuration files function-;http.conf, this is an appropriate include directive:

http {
include conf.d/*-http.conf;
}

For reference purposes, the full configuration files are also provided in this document:

We recommend, however, that you do not copy text directly from this document. It does not necessarily use the same mechanisms for positioning text (such as line breaks and white space) as text editors do. In text copied into an editor, lines might run together and indenting of child statements in configuration blocks might be missing or inconsistent. The absence of formatting does not present a problem for NGINX or NGINX Plus, because (like many compilers) they ignore white space during parsing, relying solely on semicolons and curly braces as delimiters. The absence of white space does, however, make it more difficult for humans to interpret the configuration and modify it without making mistakes.

About Reloading Updated Configuration

We recommend that each time you complete a set of updates to the configuration, you run the nginx-t command to test the configuration file for syntactic validity.

Configuring Virtual Servers for HTTP and HTTPS Traffic

These directives define virtual servers for HTTP and HTTPS traffic in separate server blocks in the top‑level http configuration block. All HTTP requests are redirected to the HTTPS server.

Configure a server block that permanently redirects requests received on port 80 for http://example.com to the HTTPS server, which is defined in the next step.

If you’re not using SSL/TLS for client connections, omit or comment out the location block. When instructed in the remainder of this guide to add directives to the server block for HTTPS traffic, add them to this server block instead.

Configuring Basic Load Balancing

To configure load balancing, you first create a named “upstream group,” which lists the backend servers. You then set up NGINX or NGINX Plus as a reverse proxy and load balancer by referring to the upstream group in one or more proxy_pass directives.

Configure an upstream group called nodejs with two Node.js application servers listening on port 8080, one on IP address 192.168.33.11 and the other on 192.168.33.12. The servers in the upstream group handle only HTTP and HTTPS traffic, because we’re creating the upstream group in the http block. For information about WebSocket, see Configuring Load Balancing of WebSocket Traffic.

By default, NGINX and NGINX Plus use the Round Robin algorithm for load balancing among servers. The load balancer runs through the list of servers in the upstream group in order, forwarding each new request to the next server. In our example, the first request goes to 192.168.33.11, the second to 192.168.33.12, the third to 192.168.33.11, and so on. For information about the other available load‑balancing algorithms, see Application Load Balancing with NGINX Plus.

Configuring Basic Session Persistence

If your application requires basic session persistence (also known as sticky sessions), you can implement it in open source by using the IP Hash load‑balancing algorithm. (NGINX Plus offers a more sophisticated form of session persistence, as described in Configuring Advanced Session Persistence.)

With the IP Hash algorithm, for each request NGINX calculates a hash based on the client’s IP address, and associates the hash with one of the upstream servers. It sends all requests with that hash to that server, thus establishing session persistence.

If the client has an IPv6 address, the hash is based on the entire address. If it has an IPv4 address, the hash is based on just the first three octets of the address. This is designed to optimize for ISP clients that are assigned IP addresses dynamically from a subnetwork (/24) range. However, it is not effective in these cases:

The majority of the traffic to your site is coming from one forward proxy or from clients on the same /24 network, because in that case IP Hash maps all clients to the same server.

A client’s IP address can change during the session, for example when a mobile client switches from a WiFi network to a cellular one.

You can also use the Hash load balancing method for session persistence, with the hash based on any combination of text and NGINX variables you specify. For example, you can hash on full (four‑octet) client IP addresses with the following configuration.

Configuring Load Balancing of WebSocket Traffic

The WebSocket protocol (defined in RFC 6455) enables simultaneous two‑way communication over a single TCP connection between clients and servers, where each side can send data independently from the other. To initiate the WebSocket connection, the client sends a handshake request to the server, upgrading the request from standard HTTP to WebSocket. The connection is established if the handshake request passes validation, and the server accepts the request. When a WebSocket connection is created, a browser client can send data to a server while simultaneously receiving data from that server.

The Node.js app server supports WebSocket out of the box, so you don’t need to amend the Node.js configuration to enable it. If you want to use NGINX or NGINX Plus to proxy WebSocket traffic to your Node.js application servers, add the directives discussed in this section.

NGINX and NGINX Plus by default use HTTP/1.0 for upstream connections. To be proxied correctly, WebSocket connections require HTTP/1.1 along with some other configuration directives that set HTTP headers:

The first proxy_set_header directive is needed because the Upgrade request header is hop‑by‑hop; that is, the HTTP specification explicitly forbids proxies from forwarding it. This directive overrides the prohibition.

The second proxy_set_header directive sets the Connection header to a value that depends on the test in the map block: if the request has an Upgrade header, the Connection header is set to upgrade; otherwise, it is set to close.

Configuring Content Caching

Caching responses from your Node.js app servers can both improve response time to clients and reduce load on the servers, because eligible responses are served immediately from the cache instead of being generated again on the server. There are a variety of useful directives that can be used to fine‑tune caching behavior; for a detailed discussion, see A Guide to Caching with NGINX.

To enable basic caching of responses from the Node.js app server, add the following configuration:

Include the proxy_cache_path directive to create the local disk directory /tmp/NGINX_cache/ for use as a cache. The keys_zone parameter allocates 10 megabytes (MB) of shared memory for a zone called backcache, which is used to store cache keys and metadata such as usage timers. A 1‑MB zone can store data for about 8,000 keys.

By default, the cache key is similar to this string of NGINX variables: $scheme$proxy_host$request_uri. To change the list of variables, specify them with the proxy_cache_key directive. One effective use of this directive is to create a cache key for each user based on the JSESSIONID cookie. This is useful when the cache is private, for example containing shopping cart data or other user‑specific resources. Include the JSESSIONID cookie in the cache key with this directive:

Configuring HTTP/2 Support

HTTP/2 is fully supported in both NGINX 1.9.5 and later, and NGINX Plus R7 and later. As always, we recommend you run the latest version of software to take advantage of improvements and bug fixes.

If using NGINX, note that in NGINX 1.9.5 and later the SPDY module is completely removed from the NGINX codebase and replaced with the HTTP/2 module. After upgrading to version 1.9.5 or later, you can no longer configure NGINX to use SPDY. If you want to keep using SPDY, you need to compile NGINX from the sources in the NGINX 1.8.x branch.

In NGINX Plus R8 and later, NGINX Plus supports HTTP/2 by default, and does not support SPDY.

In NGINX Plus R11 and later, the nginx-plus package continues to support HTTP/2 by default, but the nginx‑plus‑extras package available in previous releases is deprecated by dynamic modules.

For NGINX Plus R8 through R10, the nginx-plus and nginx-plus-extras packages support HTTP/2 by default.

If using NGINX Plus R7, you must install the nginx-plus-http2 package instead of the nginx-plus or nginx-plus-extras package.

To verify that HTTP/2 translation is working, you can use the “HTTP/2 and SPDY indicator” for Google Chrome and the “HTTP/2 indicator” for Firefox.

Full Configuration for Basic Load Balancing

The full configuration for basic load balancing appears here for your convenience. It goes in the http context. The complete file is available for download from the NGINX, Inc. website.

We recommend that you do not copy text directly from this document, but instead use the method described in Creating and Modifying Configuration Files to include these directives in your configuration – add an include directive to the http context of the main nginx.conf file to read in the contents of /etc/nginx/conf.d/nodejs-basic.conf.

Configuring Advanced Session Persistence

NGINX Plus has more sophisticated session persistence methods than open source NGINX, implemented in three variants of the sticky directive. In the following example, we add the stickycookie directive to the upstream group we created in Configuring Basic Load Balancing.

Remove or comment out the ip_hash directive, leaving only the server directives:

With this method, NGINX Plus adds an HTTP session cookie to the first response to a given client from the upstream group, identifying which server generated the response (in an encoded fashion). Subsequent requests from the client include the cookie value and NGINX Plus uses it to route the request to the same upstream server, thereby achieving session persistence.

The first parameter to stickycookie (in the example, srv_id) sets the name of the cookie to be set or inspected. The expires parameter tells the browser how long the cookie is valid, here one hour. The domain parameter defines the domain and the path parameter defines the URL path for which the cookie is set.

The zone directive creates a shared memory zone for storing information about sessions. The amount of memory allocated – here, 64 KB – determines how many sessions can be stored at a time (the number varies by platform). The name assigned to the zone – here, nodejs – must be unique for each sticky directive.

Configuring Application Health Checks

Health checks are out‑of‑band HTTP requests sent to a server at fixed intervals. They are used to determine whether a server is responsive and functioning correctly, without requiring an actual request from a client.

Here we configure NGINX Plus to send an out‑of‑band request for the top‑level URI / (slash) to each of the servers in the nodejs upstream group every 5 seconds (that URI and frequency are the defaults). If a server does not respond correctly, it is marked down and NGINX Plus stops sending requests to it until it passes a subsequent health check. We include the match parameter to define a nondefault set of health‑check tests.

Because the health_check directive is placed in the location block, we can enable different health checks for each application.

In the location block (created in Configuring Basic Load Balancing) that matches HTTPS requests in which the path starts with /webapp/, add the health_check directive:

In the http context, include a match directive to define the tests that a server must pass to be considered functional. In this example, it must return status code 200, the Content-Type response header must contain text/html, and the response body must match the indicated character string.

In the nodejs upstream group, add the following zone directive as necessary (if you configured advanced session persistence you already added it). It creates a shared memory zone that stores the group’s configuration and run‑time state, which are accessible to all worker processes.

NGINX Plus also has a slow‑start feature that is a useful auxiliary to health checks. When a failed server recovers, or a new server is added to the upstream group, NGINX Plus slowly ramps up the traffic to it over a defined period of time. This gives the server time to “warm up” without being overwhelmed by more connections than it can handle as it starts up. For more information, see the NGINX Plus Admin Guide.

For example, to set a slow‑start period of 30 seconds for your Node.js application servers, include the slow_start parameter to their server directives:

Enabling Live Activity Monitoring

NGINX Plus includes a Status module for live activity monitoring that tracks key load and performance metrics in real time. The module includes a built‑in dashboard that graphically displays the statistics, along with a RESTful JSON API that makes it very easy to feed the data to a custom or third‑party monitoring tool. These instructions show how to configure NGINX to enable the Status module and display the dashboard.

Customize the file for your deployment as specified by comments in the file. In particular, the default settings in the file allow anyone on any network to access the dashboard. We strongly recommend that you restrict access to the dashboard with one or more of the following methods:

IP address‑based access control lists (ACLs). In the sample configuration file, uncomment the allow and deny directives, and substitute the address of your administrative network for 10.0.0.0/8. Only users on the specified network can access the dashboard.

HTTP basic authentication. In the sample configuration file, uncomment the auth_basic and auth_basic_user_file directives and add user entries to the /etc/nginx/users file (for example, by using an htpasswd generator). If you have an Apache installation, another option is to reuse an existing htpasswd file.

Configuring the HTTP API Method

To enable on‑the‑fly reconfiguration of your upstream group of Node.js app servers using the API, you need to grant secured access to the upstream_conf handler. You can use the upstream_conf handler to add or remove servers, dynamically alter their weights, and set their status as primary, backup, or down.

In the server block for HTTPS traffic (created in Configuring Virtual Servers for HTTP and HTTPS Traffic), add a new location block for the on‑the‑fly reconfiguration API. It contains the upstream_conf directive (upstream_conf is also the conventional name for the location, as used here).

We strongly recommend that you restrict access to the location so that only authorized administrators can access the reconfiguration API. The allow and deny directives in the following example permit access only from the localhost address (127.0.0.1).

Configuring the DNS Method

In the http block, add the resolver directive pointing to your DNS server and then add the resolve parameter to the server directive, which instructs NGINX Plus to periodically re‑resolve the domain name (example.com here) with DNS:

NGINX Plus Release 9 and later can also use the additional information in DNS SRV records, such as the port number. Include the service parameter to the server directive, along with the resolve parameter:

Full Configuration for Enhanced Load Balancing

The full configuration for enhanced load balancing appears here for your convenience. It goes in the http context. The complete file is available for download from the NGINX, Inc. website.

We recommend that you do not copy text directly from this document, but instead use the method described in Creating and Modifying Configuration Files to include these directives in your configuration – namely, add an include directive to the http context of the main nginx.conf file to read in the contents of /etc/nginx/conf.d/nodejs-enhanced.conf.

Note: The upstream block in this configuration summary and the downloadablenodejs-enhanced.conf file is for the API method of on‑the‑fly reconfiguration. If you want to use the DNS method instead, make the appropriate changes to the block. (You can also remove or comment out the directives for the upstream_conf handler in that case, but they do not conflict with using the DNS method.)

Summary

NGINX and NGINX Plus can both be used to effectively load balance Node.js application servers, and NGINX Plus provides enhanced features to help you better manage and monitor your Node.js environment. For further information about NGINX and NGINX Plus, please see the following: