Node.js is an open source JavaScript runtime for building server-side and network applications, and it runs on Linux, macOS, FreeBSD and Windows. Starting it from the command line is fine for development. It is not how you run anything in production, because the moment your SSH session ends, so does the application.
This walkthrough sets it up properly instead: running as a managed service that survives a crash and comes back after a reboot, with Nginx in front of it.
We will setup production-ready Node.js environment on Ubuntu 20.04 server. We will use PM2 to managed and run a Node.js application, and provide users access to the application through an Nginx web server reverse proxy directive.
Two pieces there, doing two different jobs, and it is worth being clear on which is which.
PM2 keeps the process alive. It restarts the app if it exits, brings it back after a reboot, and gives you logs in one place instead of scattered across terminal sessions.
Nginx sits in front and forwards requests through. That matters for a few reasons. Node listens on a high port like 3000, and binding straight to port 80 would mean running it as root, which is exactly what you do not want an application process doing. Nginx also handles TLS, serves static files far more efficiently than Node will, and lets you run several applications behind one address.
Initial Setup
Start by updating the packages to the latest version available.
sudo apt update sudo apt upgrade
Install Specific Node.js version
You can install specific Node.js version from the Node.js repositories.
$ curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
## Installing the NodeSource Node.js 14.x repo...
## Populating apt-get cache...
+ apt-get update
Hit:1 http://us-east-2.ec2.archive.ubuntu.com/ubuntu focal InRelease
Hit:2 http://us-east-2.ec2.archive.ubuntu.com/ubuntu focal-updates InRelease
Hit:3 http://us-east-2.ec2.archive.ubuntu.com/ubuntu focal-backports InRelease
Hit:5 http://security.ubuntu.com/ubuntu focal-security InRelease
Hit:4 https://packages.cloud.google.com/apt kubernetes-xenial InRelease
Reading package lists... Done
## Confirming "focal" is supported...
+ curl -sLf -o /dev/null 'https://deb.nodesource.com/node_14.x/dists/focal/Release'
## Adding the NodeSource signing key to your keyring...
+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | gpg --dearmor | tee /usr/share/keyrings/nodesource.gpg >/dev/null
## Creating apt sources list file for the NodeSource Node.js 14.x repo...
+ echo 'deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_14.x focal main' > /etc/apt/sources.list.d/nodesource.list
+ echo 'deb-src [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_14.x focal main' >> /etc/apt/sources.list.d/nodesource.list
## Running `apt-get update` for you...
+ apt-get update
Hit:1 http://us-east-2.ec2.archive.ubuntu.com/ubuntu focal InRelease
Hit:2 http://us-east-2.ec2.archive.ubuntu.com/ubuntu focal-updates InRelease
Hit:3 http://us-east-2.ec2.archive.ubuntu.com/ubuntu focal-backports InRelease
Hit:4 http://security.ubuntu.com/ubuntu focal-security InRelease
Get:5 https://deb.nodesource.com/node_14.x focal InRelease [4583 B]
Hit:6 https://packages.cloud.google.com/apt kubernetes-xenial InRelease
Get:7 https://deb.nodesource.com/node_14.x focal/main amd64 Packages [775 B]
Fetched 5358 B in 1s (10.3 kB/s)
Reading package lists... Done
## Run `sudo apt-get install -y nodejs` to install Node.js 14.x and npm
## You may also need development tools to build native addons:
sudo apt-get install gcc g++ make
## To install the Yarn package manager, run:
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
Now install the NodeJS app with specific version.
$ sudo apt install nodejs
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
nodejs
0 upgraded, 1 newly installed, 0 to remove and 46 not upgraded.
Need to get 25.4 MB of archives.
After this operation, 123 MB of additional disk space will be used.
Get:1 https://deb.nodesource.com/node_14.x focal/main amd64 nodejs amd64 14.19.1-deb-1nodesource1 [25.4 MB]
Fetched 25.4 MB in 1s (46.5 MB/s)
Selecting previously unselected package nodejs.
(Reading database ... 149395 files and directories currently installed.)
Preparing to unpack .../nodejs_14.19.1-deb-1nodesource1_amd64.deb ...
Unpacking nodejs (14.19.1-deb-1nodesource1) ...
Setting up nodejs (14.19.1-deb-1nodesource1) ...
Processing triggers for man-db (2.9.1-1) ...
Verify Services
$ node -v v14.19.1 $ npm -v 6.14.16 $ sudo apt install build-essential
Creating a Node.js Application
$ vim app.js
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
Run and verify NodeJS app with the following command.
$ node app.js
Server running at http://localhost:3000/
$ curl http://localhost:3000
Hello World!
Installing PM2
Next let’s install PM2, a process manager for Node.js applications. PM2 makes it possible to daemonize applications so that they will run in the background as a service.
Use npm to install the latest version of PM2 on your server:
Daemonize is the important word. Without it the application is tied to your terminal and dies with it. PM2 detaches it, watches it, and restarts it when it falls over, which applications do.
The part people forget is the startup step. PM2 keeping a process alive is not the same as PM2 itself coming back after a reboot, and those are two separate pieces of configuration. Skip the second and the setup works perfectly right up until the server restarts.
$ sudo npm install pm2@latest -g
npm WARN deprecated uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
/usr/bin/pm2 -> /usr/lib/node_modules/pm2/bin/pm2
/usr/bin/pm2-dev -> /usr/lib/node_modules/pm2/bin/pm2-dev
/usr/bin/pm2-docker -> /usr/lib/node_modules/pm2/bin/pm2-docker
/usr/bin/pm2-runtime -> /usr/lib/node_modules/pm2/bin/pm2-runtime
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.3.2 (node_modules/pm2/node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
+ pm2@5.2.0
added 182 packages from 182 contributors in 10.129s
Now start the app.js application with PM2.
$ pm2 start app.js
[PM2] Starting /home/ubuntu/app.js in fork_mode (1 instance)
[PM2] Done.
┌─────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ app │ default │ N/A │ fork │ 3234934 │ 0s │ 0 │ online │ 0% │ 26.0mb │ ubuntu │ disabled │
└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
Verify Node JS Running Port
$ sudo netstat -tulnp | grep 3000 tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN 3234934/node /home/ You can stop NodeJS app with the following command. $ pm2 stop app.js [PM2] Applying action stopProcessId on app [app.js](ids: [ 0 ]) [PM2] [app](0) ✓ ┌─────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐ │ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │ ├─────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤ │ 0 │ app │ default │ N/A │ fork │ 0 │ 0 │ 0 │ stopped │ 0% │ 0b │ ubuntu │ disabled │ └─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
Install Nginx Web Server
sudo apt update sudo apt install nginx systemctl status nginx
Create a Virtual Host
sudo vim /etc/hosts
IP - Domain
sudo vim /etc/nginx/sites-available/app.example.com
server {
server_name app.example.com;
index index.html index.htm;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable newly created virtual host.
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo pm2 start next
sudo pm2 restart next
sudo pm2 log next
sudo systemctl restart nginx.service
sudo systemctl status nginx.service
Verify Node JS service after applying ProxyPass
$ curl app.example.com Hello World
Conclusion
Reference
https://nodejs.org/en/


