A late post this time. I spent a good part of the past week figuring out how to deploy one or more Socket.IO-based Node.js servers using Nginx. Since it was about deployment, SSL was an important factor too. I write this post because of the sheer amount of Googling and trial-and-error I had to go through before I finally had a solution working. The primary problems in finding a straightforward solution via search engines were-

1. SSL – Especially since my application requires re-direction of POST requests as they are; something a simple rewrite command in Nginx wouldn’t accomplish.

2. Socket servers – Deploying a simple Node.js server is one thing, deploying a socket-based server is something different altogether.

3. Multiple Socket-servers – This was undoubtedly the trickiest part. To re-direct the incoming request to the appropriate server, and present it to the server in a format it understands, was the most difficult job (atleast for me). I experimented with a lot of Nginx rewriting, Node.js namespacing, etc., before I finally found an answer that worked.

So, here goes the procedure involved….

Step 1. Write your Node.js code(obviously)

For the sake of this tutorial, I will use two simple echo servers. Each is programmed to listen to its own port.

Server-1 (node1.js)

//PORT to connect to const PORT = 3001; //Instantiate socket server var app = require('http').createServer().listen(PORT); var io = require('socket.io').listen(app); //Simple echo server io.on('connection', function(socketconnection){ socketconnection.send("Connected to Server-1"); socketconnection.on('message', function(message){ socketconnection.send(message); }); });

Server-2 (node2.js)

//PORT to connect to const PORT = 3002; //Instantiate socket server var app = require('http').createServer().listen(PORT); var io = require('socket.io').listen(app); //Simple echo server io.on('connection', function(socketconnection){ socketconnection.send("Connected to Server-2"); socketconnection.on('message', function(message){ socketconnection.send(message); }); });



Step 2. Install PM2 for running Node.js servers

There are many options for this; I use PM2 for its simple commands and nice-looking interface. PM2 also helps ‘watch’ the Node.js servers you deploy, so that they can be restarted in case of failure or code changes. It also offers many other options for the way you want your server to function, but I won’t go into those details here.

Install PM2 using

npm install pm2 -g

Once installed, you can start your servers(with watching enabled) as follows:

pm2 start --watch node1.js

pm2 start --watch node2.js

Depending on your setup, you might have to use sudo with the above commands. Click here if you want to know more PM2 commands.



Step 3. Install and start Nginx

Pretty straight-forward.

sudo apt-get install nginx

sudo service nginx start

Step 4. Get your SSL certificates

You can either use un-verified self-signed certificates (good for development/testing), or buy ones from someplace like Comodo (essential for deployment).

To generate self-signed ones, do

sudo mkdir /etc/nginx/ssl

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

This will put your certificates in /etc/nginx/ssl .



Step 5. Configure Nginx

Make a file called server_nginx.conf and put the following code in it-

#Upstream Node Server-1 upstream node1 { server 127.0.0.1:3001; } #Upstream Node Server-2 upstream node2 { server 127.0.0.1:3002; } #To redirect all HTTP traffic(keeping requests like POST intact) #to HTTPS server { listen 80; server_name localhost; location / { return 307 https://localhost$request_uri; } } #The actual HTTPS server server { listen 443; ssl on; server_name localhost; #SSL certificates ssl_certificate /etc/nginx/ssl/nginx.crt; ssl_certificate_key /etc/nginx/ssl/nginx.key; #For Server-1 location /server1/ { #Configure proxy to pass data to upstream node1 proxy_pass http://node1/socket.io/; #HTTP version 1.1 is needed for sockets proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } #For Server-2 location /server2/ { #Configure proxy to pass data to upstream node2 proxy_pass http://node2/socket.io/; #HTTP version 1.1 is needed for sockets proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }

You will now have to make Nginx aware of this configuration by symlinking this file into /etc/nginx/sites-enabled . To do that, do

sudo ln -s /path/to/server_nginx.conf /etc/nginx/sites-enabled/

Then restart Nginx

sudo service nginx restart

Thats about it. But before we go to the client code, a few points to note about the above Nginx .conf file:

1. You can use rewrite on line 18; I do it this way because it preserves non-GET requests as they are. If you know a better way to go about it, do let me know :-).

2. Defining the Node.js servers in upstream blocks is good practice, especially since it helps do easy load-balancing between identical Node.js servers in the future. More on that here.

3. If you buy SSL certificates from a vendor, you can modify lines 30 and 31 as necessary.

4. Observe lines 36 and 46. The trailing /socket.io/ (including the trailing backslash) is essential, since it replaces the original URI and expresses the request in a format that Socket.IO will understand.

5. Depending on your application, you might have to add more code to the .conf file. For example, if your application involves long intervals where no reading takes place on the socket connection, you might have to increase the proxy read-timeout period.

6. Ofcourse, during actual deployment, you will have to replace locahost with your actual domain name.

Finally, heres a client code to connect to a server and send a message:

var io = require('socket.io-client'); var conn = io.connect('https://localhost', {path: '/server1'}); conn.emit('message', 'Some message'); conn.on('message', function (data){ console.log(data); });

Note the ‘path’ parameter thats conveyed during connection, and the explicit use of https in the address.

Thats it for this time :-D. Hope this helps someone, and saves him/her the trouble of searching the internet for a complete day trying to accomplish (what is really) a trivial thing. Thanks for reading!