Skip to main content

Hosting & Deploying NodeJS Apps on Ubuntu


Doing local development with Node is simple. All you have to do is node app.js in the folder where your source code is and your application is working.
Where things get complicated is when you want to put your app in production, on a web server for the whole world to admire it.
If you are coming from PHP or Ruby on Rails you might be used to having a very simple way of hosting & deploying your application. All you have to do is put your code into a specific folder. Every time you have an update, just replace the code. It just works.
To learn more about Hosting & Deploying NodeJS Apps on Ubuntu and other great features of NodeJS, you can join a free demo on Nodejs Online Training
Let’s look at how you can host and deploy a production-ready NodeJs application.
For this purpose, we will need SSH access to a freshly installed server. For this tutorial, we will look at doing this on Ubuntu, but all steps are easily reproducible on CentOS or any other flavor of Linux.
Your server can be running in AWS, Rackspace or even in your local VirtualBox. It doesn’t really matter, the steps are always the same.

Getting your hands dirty

I know that you are impatient to learn what your setup will be, so let’s get this out of the way.
First, we will use the Nginx webserver to handle all requests from the web.
Requests for static content will be handled directly by Nginx. If we need to support SSL, this will also be handled by Nginx. All other web requests will be handled again by Nginx but forwarded to our Node application code.
Second, to ensure that our node application is always on, even when the application crashes or the server is restarted, we will use upstart or systemd tasks depending on what is available on your system of choice.
Last, we will launch as many instances of our application as cores and cpus have our server machine. The purpose of this is to use the maximum of the available resources. Each instance will listen for requests on a separate port and Nginx will forward the appropriate requests.
If you don’t understand this setup or you’ve never heard some of its components. Don’t worry, everything will be explained in details below.
On the other hand, if all of this is very clear to you, you can skip the next few paragraphs and go directly to the actual commands and files that we will use to make it all work.
Take your career to new heights of success with Nodejs Course

What does it mean to host an app?

Just like on your development machine you can run node app.js on your server and your code will be executed perfectly.
However, this is far from ideal and there are many problems with it. Let’s look at just a few of them.
Serving static files, like JavaScript, images & CSS, can be done with node but it is not very efficient. It may use too much memory and it might not cache frequently accessed files.
Establishing secure SSL connection is not simple, yet most web applications require it. There are certificate files to handle as well as other small details. Moreover, your application code does not care whether the connection is secure or not, as this will only add unnecessary complication to your logic.
Limiting file uploads is critical for any app which allows file uploading. Otherwise, unintentionally or intentionally a user may try to upload a 10GB (or much bigger) file and crash your server. Implementing this efficiently in Node is hard.
These are just few of the reasons, and there are many more, why you should have something else in front of your node application which will handle user requests.
It should serve static files, establish secure connections, as well as other things and decide when a request should be handled by your application code and when not.
Nginx is one of the most widely used web servers. It can do all of the above and lot more. In addition, it is very easy to setup. That is why we are going to use it.
To get in-Depth knowledge on Nodejs you can enroll for a live demo on Nodejs Certification

Use all available resources

Node is famous for being a single-threaded process. What this means is that no two things happen at the same time. If your server has multiple cores or processors it will only use one of them.
This is not very efficient. If you can use all of the cores you will be able to handle higher load with the same server and as a result, save some money.
One solution is to write some additional code. Node ships with a cluster module which can handle the situation described above.
However, I believe it is better to keep in Node only your application logic, and handling multiple processes is not part of that.
Instead all you have to do on your server is run node app.js multiple times, each time providing a different predefined port the app to listen to. Then Nginx can forward requests to each of these ports.

Always On

The next problem that you will face is to keep your app always on. It may crash or your server may restart or something else could happen. You need to ensure that no matter what, your app is always running.
Moreover, this should happen automatically. You don’t want to wake up in the middle of the night, just because your server restarted due to temporarily power outage (this happens even on Amazon or Rackspace servers).
Fortunately, all Linux instances come with what is called an init system. This are robust systems which can monitor the status of your application, restart it when certain conditions are met and start it when the server itself is started.
In the past, using the init system required writing complicated scripts and everybody hated it.
These days, most modern Linux distributions come with a modern init system called “systemd” which is very easy to use. The only exception until recently was Ubuntu, which up to version 14.10 came with “upstart”, which is also easy to use. Beginning with version 15.04 Ubuntu also uses “systemd”.

Deploying

Now that you know how you are going to run your code, the next question is how you are going to put your code on your server.
We are going to use Git. It has many great features but we are going to use just a few of them, mainly it’s the ability to push changes between computers.
Now that you know how it should work in theory, let’s look at how it is done in practice.

Install

First, you will need to install all necessary packages on your server.

Nginx

Run the following in your Linux shell.
sudo -s
nginx=stable
add-apt-repository ppa:nginx/$nginx
apt-get update
apt-get install nginx
exit
This will install the latest stable version of Nginx on your Ubuntu server.

Node & NPM

curl -sL https://deb.nodesource.com/setup | sudo bash -
sudo apt-get install nodejs
sudo apt-get install build-essential
This will install the latest versions of Node & NPM. The tools in build-essential are required by some npm modules when installing.

Git

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git
After this, you will have the latest version of Git on your Ubuntu.

Putting some code on your server

For the purpose of this article, we are going to use our base-express repository. It’s a repository that anyone can use to start his Node web project with the Express web framework.
cd /opt/
mkdir app
chown your_user_name app
git clone https://github.com/terlici/base-express.git app
cd app
npm install
I prefer using /opt to contain my application files, but you can choose any other folder.
Now our app is ready to run and all of its modules are installed.

Little customization

Let’s customize a little bit our application. You will see later why.
First, replace the app.js file in the root of the folder
var express = require('express')
  , app = express()
  , port = process.env.PORT || 3000

app.engine('jade', require('jade').__express)
app.set('view engine', 'jade')

app.use(require('./controllers'))

app.listen(port, function() {
  console.log('Listening on port ' + port)
})
Now our application has a default port to listen to, but can also listen to any other provided by the environment. In addition, it can no more serve by itself its static files in the public/ folder.
Next, please replace the views/index.jade with the following
doctype html
html
  head
    title Your basic web app structure
    link(href="/public/css/style.css", rel="stylesheet")
  body
    h1 Welcome to your basic web app structure
    p
      | If the title above is red
      | then Nginx is serving static files!
The last file to change is public/css/styles.css
h1 {
  color: red;
}

Running 24x7

Now that we have our application ready, how do we start it and keep it always running?
Up until version 14.10 of Ubuntu, the default init system is upstart. From Ubuntu 15.10 and Debian 8, the default init system is systemd.
Upstart
Let’s see how we can run our application forever with upstart. In upstart there are jobs. Each job describes how your application will be running.
Here is the upstart job for the node app
description "App Server"
author "Stefan Fidanov <fidanov@terlici.com>"

start on (filesystem and net-device-up IFACE=lo)
stop on runlevel [!2345]

respawn

env PORT=5000

chdir /opt/app/
exec node app.js
The start on line ensures that your app will start when the server starts and the file system and network are loaded. The respawn ensures that if your app instance dies for whatever, then it will be launched again.
Put this in /etc/init/node-app-1.conf. Then take the same file, replace env PORT=5000 with env PORT=5001 and save it as /etc/init/node-app-2.conf.
Now you have two upstart jobs node-app-1 and node-app-2. Each can look after one instance of your application. You need to create as many as processor cores you have on your server. For example, if you have two cpus, each with the cores, then you need 4. For the purpose of this article, we will imagine that we have 1 CPU with 2 cores.
You can run your app instances with
$ sudo start node-app-1
$ sudo start node-app-2
Your app will handle requests at the ports 5000 & 5001. If one of them crashes or the server is restarted, they will be also restarted. Your application is now really always on.
Systemd
Since Ubuntu 15.04 and Debian 8, systemd is the default init system. Systemd has services. Each service describes how an application is running.
Let’s have a look at how the service for our app will look like
[Service]
ExecStart=/usr/bin/node /opt/app/app.js

Restart=always

StandardOutput=syslog

StandardError=syslog

SyslogIdentifier=node-app-1

User=your_app_user_name

Group=your_app_user_name

Environment=NODE_ENV=production PORT=5000


[Install]
WantedBy=multi-user.target
Put this in /etc/systemd/system/node-app-1.service but don’t forget to replace your_app_user_name with the appropriate user name.
Then create another file with the name /etc/systemd/system/node-app-2.service and put the same content but replace 5000 with 5001 and node-app-1 with node-app-2.
These two services describe how each of our app instances should run and where their outputs should go. When the app is killed it will automatically be restarted.
$ sudo systemctl start node-app-1
$ sudo systemctl start node-app-2
These two lines will run our app instances and keep them alive.
To make them also start when the server starts or restarts do the following
$ sudo systemctl enable node-app-1
$ sudo systemctl enable node-app-2
Systemd will now run and keep alive forever both instance of our apps.

Re-deploying your app

With the current setup, if we have some new application code in our repository, all you have to do is the following
$ cd /opt/app
$ git pull
And then if you have upstart do
$ sudo restart node-app-1
$ sudo restart node-app-2
Or if you have systemd do
$ sudo systemctl restart node-app-1
$ sudo systemctl restart node-app-2
Afterwards, the latest version will be ready to serve your users.

Configure Nginx

Listening on ports 5000 & 5001 is nice but by default, browsers are looking at port 80. Also in our current setup, no static files are served by our application.
Here is our nginx configuration
upstream node_server {
   server 127.0.0.1:5000 fail_timeout=0;
   server 127.0.0.1:5001 fail_timeout=0;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    index index.html index.htm;

    server_name _;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_redirect off;
        proxy_buffering off;
        proxy_pass http://node_server;
    }

    location /public/ {
        root /opt/app;
    }
}
This configuration will make available all static files from /opt/app/public/ at the /public/ path. It will forward all other requests to the two instances of our app listening at the ports 5000 and 5001. Basically, Nginx is both a web server and load balancer.
To use this configuration save it in /etc/nginx/sites-available/node-app and then do the following:
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/node-app /etc/nginx/sites-enabled/node-app
sudo /etc/init.d/nginx restart
This will remove the current default configuration, then it will make active the configuration we just implemented and finally it will restart nginx so that the latest configuration is loaded.

Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...