How To Set Up a Node.js Application for Production on Ubuntu 16.04

How To Set Up a Node.js Application for Production on Ubuntu 16.04
Not using Ubuntu 16.04?Choose a different version or distribution.
Ubuntu 16.04


Node.js is an open-source JavaScript runtime environment for building server-side and networking applications. The platform runs on Linux, MacOS, FreeBSD, and Windows. Node.js applications can be run at the command line, but we’ll focus on running them as a service, so that they will automatically restart on reboot or failure, and can safely be used in a production environment.

In this tutorial, we will cover setting up a production-ready Node.js environment on a single Ubuntu 16.04 server. This server will run a Node.js application managed by PM2, and provide users with secure access to the application through an Nginx reverse proxy. The Nginx server will offer HTTPS, using a free certificate provided by Let’s Encrypt.


This guide assumes that you have the following:

When you’ve completed the prerequisites you will have a server serving the default Nginx placeholder page at https://example.com/.

Let’s get started by installing the Node.js runtime on your server.

Step 1 — Installing Node.js

We will install the latest LTS release of Node.js, using the NodeSource package archives.

First, you need to install the NodeSource PPA in order to get access to its contents. Make sure you’re in your home directory, and use curl to retrieve the installation script for the Node.js 16.x archives:

  1. cd ~
  2. curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh

You can inspect the contents of this script with nano (or your preferred text editor):

  1. nano nodesource_setup.sh

Then run the script under sudo:

  1. sudo bash nodesource_setup.sh

The PPA will be added to your configuration and your local package cache will be updated automatically. After running the setup script from nodesource, you can install the Node.js package in the same way that you did above:

  1. sudo apt-get install nodejs

The nodejs package contains the node binary as well as npm, so you don’t need to install npm separately. However, in order for some npm packages to work (such as those that require compiling code from source), you will need to install the build-essential package:

  1. sudo apt-get install build-essential

The Node.js runtime is now installed, and ready to run an application. Let’s write a Node.js application.

Step 2 — Creating a Node.js Application

We will write a Hello World application that returns “Hello World” to any HTTP requests. This is a sample application that will help you get your Node.js set up, which you can replace with your own application — just make sure that you modify your application to listen on the appropriate IP addresses and ports.

Hello World Code

First, create and open your Node.js application for editing. For this tutorial, we will use nano to edit a sample application called hello.js:

  1. cd ~
  2. nano hello.js

Insert the following code into the file. If you want to, you may replace the highlighted port, 8080, in both locations (be sure to use a non-admin port, i.e. 1024 or greater):

#!/usr/bin/env nodejs
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8080, 'localhost');
console.log('Server running at http://localhost:8080/');

Now save and exit.

This Node.js application listens on the specified address (localhost) and port (8080), and returns “Hello World” with a 200 HTTP success code. Since we’re listening on localhost, remote clients won’t be able to connect to our application.

Test Application

In order to test your application, set hello.js to be executable using chmod:

  1. chmod +x ./hello.js

Then run it like so:

  1. ./hello.js
Server running at http://localhost:8080/

Note: Running a Node.js application in this manner will block additional commands until the application is killed by pressing Ctrl-C.

In order to test the application, open another terminal session on your server, and connect to localhost with curl:

  1. curl http://localhost:8080

If you see the following output, the application is working properly and listening on the proper address and port:

Hello World

If you do not see the proper output, make sure that your Node.js application is running, and configured to listen on the proper address and port.

Once you’re sure it’s working, switch back to your other terminal and kill the Node.js application (if you haven’t already) by pressing Ctrl+C.

Step 3 — Installing PM2

Now we will install PM2, which is a process manager for Node.js applications. PM2 provides an easy way to manage and daemonize applications (run them in the background as a service).

We will use npm, a package manager for Node modules that installs with Node.js, to install PM2 on our server. Use this command to install PM2:

  1. sudo npm install -g pm2

The -g option tells npm to install the module globally, so that it’s available system-wide.

Step 4 — Managing Applications with PM2

We will cover a few basic uses of PM2.

Start Application

The first thing you will want to do is use the pm2 start command to run your application, hello.js, in the background:

  1. pm2 start hello.js

This also adds your application to PM2’s process list, which is outputted every time you start an application:

[PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌─────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐ │ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │ ├─────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤ │ 0 │ hello │ default │ N/A │ fork │ 13734 │ 0s │ 0 │ online │ 0% │ 25.0mb │ sammy │ disabled │ └─────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

As you can see, PM2 automatically assigns a name (based on the filename, without the .js extension) and a PM2 id. PM2 also maintains other information, such as the PID of the process, its current status, and memory usage.

Applications that are running under PM2 will be restarted automatically if the application crashes or is killed, but an additional step needs to be taken to get the application to launch on system startup (boot or reboot). Luckily, PM2 provides an easy way to do this, the startup subcommand.

The startup subcommand generates and configures a startup script to launch PM2 and its managed processes on server boots:

  1. pm2 startup systemd

The last line of the resulting output will include a command that you must run with superuser privileges:

[PM2] Init System found: systemd [PM2] You have to run this command as root. Execute the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

Run the command that was generated (similar to the highlighted output above, but with your username instead of sammy) to set PM2 up to start on boot (use the command from your own output):

  1. sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

This will create a systemd unit which runs pm2 for your user on boot. This pm2 instance, in turn, runs hello.js. You can check the status of the systemd unit with systemctl:

  1. systemctl status pm2-sammy

For a detailed overview of systemd, see Systemd Essentials: Working with Services, Units, and the Journal.

Other PM2 Usage (Optional)

PM2 provides many subcommands that allow you to manage or look up information about your applications. Note that running pm2 without any arguments will display a help page, including example usage, that covers PM2 usage in more detail than this section of the tutorial.

Stop an application with this command (specify the PM2 App name or id):

  1. pm2 stop app_name_or_id

Restart an application with this command (specify the PM2 App name or id):

  1. pm2 restart app_name_or_id

The list of applications currently managed by PM2 can also be looked up with the list subcommand:

  1. pm2 list

More information about a specific application can be found by using the info subcommand (specify the PM2 App name or id):

  1. pm2 info example

The PM2 process monitor can be pulled up with the monit subcommand. This displays the application status, CPU, and memory usage:

  1. pm2 monit

Now that your Node.js application is running, and managed by PM2, let’s set up the reverse proxy.

Step 5 — Setting Up Nginx as a Reverse Proxy Server

Now that your application is running, and listening on localhost, you need to set up a way for your users to access it. We will set up the Nginx web server as a reverse proxy for this purpose.

In the prerequisite tutorial, we set up our Nginx configuration in the /etc/nginx/sites-available/default file. Open the file for editing:

  1. sudo nano /etc/nginx/sites-available/default

Within the server block you should have an existing location / block. Replace the contents of that block with the following configuration. If your application is set to listen on a different port, update the highlighted portion to the correct port number.

. . .
    location / {
        proxy_pass http://localhost:8080;
        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;

This configures the server to respond to requests at its root. Assuming our server is available at example.com, accessing https://example.com/ via a web browser would send the request to hello.js, listening on port 8080 at localhost.

You can add additional location blocks to the same server block to provide access to other applications on the same server. For example, if you were also running another Node.js application on port 8081, you could add this location block to allow access to it via http://example.com/app2:

/etc/nginx/sites-available/default — Optional
    location /app2 {
        proxy_pass http://localhost:8081;
        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;

Once you are done adding the location blocks for your applications, save and exit.

Make sure you didn’t introduce any syntax errors by typing:

  1. sudo nginx -t

Next, restart Nginx:

  1. sudo systemctl restart nginx

Assuming that your Node.js application is running, and your application and Nginx configurations are correct, you should now be able to access your application via the Nginx reverse proxy. Try it out by accessing your server’s URL (its public IP address or domain name).


Congratulations! You now have your Node.js application running behind an Nginx reverse proxy on an Ubuntu 16.04 server. This reverse proxy setup is flexible enough to provide your users access to other applications or static web content that you want to share. Good luck with your Node.js development.

DigitalOcean provides multiple options for deploying Node.js applications, from our simple, affordable virtual machines to our fully-managed App Platform offering. Easily host your Node.js application on DigitalOcean in seconds.

Learn more here

About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

In what folder should I start developing my Node.js Application? I plan on creating a MEAN (MongoDB, Express.js, Angular.js, Node.js) Application but I don’t know if I should place it on a folder like /var/www/{my_project_name} or should it be on /home/{my_username}/{my_project_name} or on /home/{dedicated_username_for_the_project}/{my_project_name} ?

I see that in this guide you put both the reverse proxy as well as the node server on one droplet. In the 14.04 guide, they used a separate droplet for the reverse proxy and a separate droplet for the node.js app.

Could you explain the rationale a bit behind when and why it would be good to host these servers on separate droplets? For example, at what point would it start to make sense to look at using the nginx server as a load balancer and have multiple app droplets running in parallel?

I’m trying to decide if we need multiple separate droplets for DB, app, nginx etc. or if we can get by with just one droplet for all three services at this stage.

pm2 startup systemd

should be change to

pm2 startup ubuntu


Hey guys! I’ve written a script that does all of this with just one line of code pasted into the terminal!


Give it a star if you like it, it would mean a lot!

Hey, just found an error:

The “server_name” line needs to appear in the HTTP block, not the HTTPS block.

Placing it in the first block produces perfect results.

Phenomenal walk through, Digital Ocean puts it down.

Why is the reverse proxy necessary or desirable? Can’t you just let node serve the app directly? Does using a proxy interfere with socket.io communication between server/client?

I am getting https : Secure on the URL bar, but the website is not loaded. I am getting this error:

** 502 Bad Gateway

nginx/1.10.0 (Ubuntu)**

Any suggestions on this one ?

While setting up the reverse proxy using localhost:

proxy_pass http://localhost:8080;

It sometimes spits out a 502 error. This may happen due an infinite loop of domain forwarding. To avoid this, use ip address instead of localhost or like as follows.


replace by your external ip address

Also, if you need to point this to a subdomain instead of main domain or any directory path:

server {
    		listen 80;
		server_name SUBDOMAIN.DOMAIN.com;
		location / {
        proxy_pass http://IP_ADDRESS:8080; //ip address or localhost or domain name. Make sure it doesn’t fall in an infinite loop of redirects. IP address is safest option but it’s a situational thing
        		        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;

For those, who using CloudFlare and getting error after Let’s Encrypt attempt, read this


  1. You don’t need to stop nginx
  2. Instead of using
$ ./letsencrypt-auto certonly --standalone


$ ./letsencrypt-auto certonly --webroot --webroot-path /var/www/YOUR_WWW_PATH/ --renew-by-default --email YOURMAIL@example.com --text --agree-tos -d YOURDOMAIN.com -d www.YOURDOMAIN.com

I’m hung up on running the Hello World app. I keep getting the error:

./hello.js: line 1: syntax error near unexpected token `('
./hello.js: line 1: `var http = require('http');'

I’ve tried inputting the code using nano and vi; and I’ve tried typing it and copy/pasting it, always deleting hello.js between attempts.

Any clues?

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel