How To Deploy Node.js Applications Using Systemd and Nginx

Introduction

Duringinstallation of any web application to a Droplet, it’s quite alluring to simply utilize the similar setup as was used during i.e. running “node server.js” in a terminal or “ruby app.rb” to start the server. This is not only easy and simple, but it also provides visible logs. One can also utilize“nohup”  or “tmux” or “screen” for keeping it running even in case of  SSH session being dropped. But it’s risky: if server crashes and there is no one to restart it what will happen?

Crontab and forever can be utilized to handle this. This guide provides a powerful although a bit complex solution. Systemd (accessible on Fedora andArch, and CentOS in the future) can be utilized for managing web applications completely: uptime,  logs, advanced daemon startup, resources and security through cgroupscan be all controlled, accessed and tweaked in a unified way.

This guide utilizes an easy Node.js application,however it is applicable to almost allothers as well, like Python, Ruby etc. For web applications of PHP, it is suggested to utilize a more specific LEMP or LAMP stack instead.

In this guide, commands will be given for both Arch and Fedora, be careful and keep a tab on various commands for avoiding confusion or misconfiguration. Unless otherwise indicated, same command can be used for both the systems. Additionally, it is suggested that you go through the complete tutorial first prior to pursuing it step-by-step, as this will help you in taking an overview as to what it contains and if it is suitable for your situation or not.

 

System Preliminaries

By default, a server with Fedora droplets and systemd. Arch Linux are configured, howeversystemd can also be deployed on different distributions as well. For this, you need to refer to your documentation: UbuntuDebian.

  • Nginx, to be utilized as a reverse-proxy http and websocket server.
  • Git, to deploy nvm, and for pulling your application in casegit is used.
  • Root access. You can login as a regular user and sudo all commands, or to su – or sudosu – to a root prompt.

Installation of packages

Arch:

# pacman -Sy
# pacman -S nginx git

Fedora:

# yum install nginx git

Application Preliminaries

You can customize these settings as per your liking; however they need to be set and approved before starting.

User

An individual user account will be used by the application to run. You can decide on any name for it, but it should be easy to maintain, remember and must relate to the application. Here srv-node-sample is utilized.

# useradd -mrU srv-node-sample

Port

In order to prevent conflicts, select a high port. We will use “15301” here.

Application Setup

Begin by deploying whatever is essential for running of the application. For Node.js (and Python, Ruby etc.), two options are there: a user-specific deployment (e.g. using nvmrbenvRVMvirtualenv, etc) or using the runtime of the system.

Using the system node

Arch:

# pacman -S nodejs

Fedora:

# yum install nodejs

Using a user-specific install

This needs to be deployed in the home directory of the application, i.e.  /home/srv-node-sample, and it can be done by easily logging in as that user:

# su srv-node-sample
$ cd
$ curl https://raw.github.com/creationix/nvm/master/install.sh | sh
$ source ~/.nvm/nvm.sh
$ nvm install 0.10
$ nvm alias default 0.10

Now take note as to where the node binary is deployed:

$ which node
/home/srv-node-sample/.nvm/v0.10.22/bin/node

Deploying your application

Install your code while being logged in to  srv-node-sample. Given below is only an example and your process may vary.

$ git clone [email protected]:user/repo.git .
$ npm install
$ grunt deploy

The sample application given below has been utilized for this guide:

var http = require('http');
http.createServer(function(req, res) {
    res.end('<h1>Hello, world.</h1>');
}).listen(15301);

Now return to root:

$ exit

Nginx Setup

This guide only covers the essential configuration in brief. For a comprehensive guide on Nginx configuration, see the nginx manual.

Put the commands below in your server block:

location / {
    proxy_pass http://localhost:15301/;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Now establish its daemon:

# systemctl enable nginx
# systemctl restart nginx

Systemd Setup


For the application, a service file needs to be created in  /etc/systemd/system/node-sample.service.

  • Also, a few variables need to be inserted:

    [node binary] This is the output of “which node” as the srv-node-sample user. Either /usr/bin/node or the ~/.nvm/... path noted above.

  • [main file] This is the major file of your application. Here, ‘index.js`.

  • Also ensure replacing srv-node-sample!

[Service]
ExecStart=[node binary] /home/srv-node-sample/[main file]
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=node-sample
User=srv-node-sample
Group=srv-node-sample
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Now begin the service:

# systemctl enable node-sample
# systemctl start node-sample

Usage

Status


~~~~

systemctl status node-sample

node-sample.service
Loaded: loaded (/etc/systemd/system/node-sample.service; enabled)
Active: active (running) since Fri 2013-11-22 01:12:15 UTC; 35s ago Main PID: 7213 (node) CGroup: name=systemd:/system/node-sample.service └─7213 /home/srv-node-sample/.nvm/v0.10.22/bin/node /home/srv-nod…

Nov 22 01:12:15 d02 systemd[1]: Started node-sample.service.
~~~~

Logs


~~~~

journalctl -u node-sample

— Logs begin at Thu 2013-11-21 19:05:17 UTC, end at Fri 2013-11-22 01:12:15 UTC. —
Nov 22 01:12:15 d02 systemd[1]: Starting node-sample.service…
Nov 22 01:12:15 d02 systemd[1]: Started node-sample.service. Nov 22 01:12:30 d02 node-sample[7213]: Sample message from application ~~~~

Restart, stop, etc


Force a restart:

# systemctl restart node-sample

Stop the application:

# systemctl stop node-sample

If the application is killed or if it dies, then it will automatically restart:

# systemctl status node-sample
node-sample.service
   Loaded: loaded (/etc/systemd/system/node-sample.service; enabled)
   Active: active (running) since Fri 2013-11-22 01:12:15 UTC; 35s ago
 Main PID: 7213 (node)
   CGroup: name=systemd:/system/node-sample.service
           └─7213 /home/srv-node-sample/.nvm/v0.10.22/bin/node /home/srv-nod...

Nov 22 01:12:15 d02 systemd[1]: Started node-sample.service.

# kill 7213

# systemctl status node-sample
node-sample.service
   Loaded: loaded (/etc/systemd/system/node-sample.service; enabled)
   Active: active (running) since Fri 2013-11-22 01:54:37 UTC; 6s ago
 Main PID: 7236 (node)
   CGroup: name=systemd:/system/node-sample.service
           └─7236 /home/srv-node-sample/.nvm/v0.10.22/bin/node /home/srv-nod...

Nov 22 01:54:37 d02 systemd[1]: node-sample.service holdoff time over, sch...t.
Nov 22 01:54:37 d02 systemd[1]: Stopping node-sample.service...
Nov 22 01:54:37 d02 systemd[1]: Starting node-sample.service...
Nov 22 01:54:37 d02 systemd[1]: Started node-sample.service.

There is a change in PID, whichactually shows that the application hasbeen killed and restarted.

Websockets

In case the application is usingwebsockets, then the lines given below need to be added to the Nginx configuration:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;

and Nginx needs to be reloaded:

# systemctl reload nginx

KB Admin has written 28 articles

Leave a Reply