Node.js is an open-source JavaScript runtime environment for easily building server-side and networking applications. The platform runs on Linux, OS X, 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 Debian 8 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.
This guide assumes that you have a Debian 8 server, configured with a non-root user with sudo
privileges, as described in the initial server setup guide for Debian 8.
It also assumes that you have a domain name, pointing at the server’s public IP address.
Let’s get started by installing the Node.js runtime on your server.
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 6.x archives:
- cd ~
- curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh
You can inspect the contents of this script with nano (or your preferred text editor):
- nano nodesource_setup.sh
And run the script under sudo
:
- 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:
- sudo apt-get install nodejs
The nodejs
package contains the nodejs
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:
- 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.
We will write a Hello World application that simply 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.
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
:
- cd ~
- 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 simply 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.
To enable us to test the application, mark hello.js
executable:
- chmod +x ./hello.js
And run it like so:
- ./hello.js
OutputServer 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
:
- curl http://localhost:8080
If you see the following output, the application is working properly and listening on the proper address and port:
OutputHello 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, kill the application (if you haven’t already) by pressing Ctrl+C.
Now we’ll 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:
- sudo npm install -g pm2
The -g
option tells npm
to install the module globally, so that it’s available system-wide.
PM2 is simple and easy to use. We will cover a few basic uses of PM2.
The first thing you will want to do is use the pm2 start
command to run your application, hello.js
, in the background:
- pm2 start hello.js
This also adds your application to PM2’s process list, which is outputted every time you start an application:
Output[PM2] Spawning PM2 daemon
[PM2] PM2 Successfully daemonized
[PM2] Starting hello.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ hello │ 0 │ fork │ 3524 │ online │ 0 │ 0s │ 21.566 MB │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────────────┴──────────┘
Use `pm2 show <id|name>` to get more details about an app
As you can see, PM2 automatically assigns an App 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. You must also specify the platform you are running on, which is ubuntu
, in our case:
- pm2 startup systemd
The last line of the resulting output will include a command that you must run with superuser privileges:
Output[PM2] You have to run this command as root. Execute the following command:
sudo env PATH=$PATH:/usr/bin /usr/local/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):
- sudo env PATH=$PATH:/usr/bin /usr/local/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
:
- systemctl status pm2
For a detailed overview of systemd, see Systemd Essentials: Working with Services, Units, and the Journal.
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
):
- pm2 stop app_name_or_id
Restart an application with this command (specify the PM2 App name
or id
):
- pm2 restart app_name_or_id
The list of applications currently managed by PM2 can also be looked up with the list
subcommand:
- pm2 list
More information about a specific application can be found by using the info
subcommand (specify the PM2 App name or id):
- pm2 info example
The PM2 process monitor can be pulled up with the monit
subcommand. This displays the application status, CPU, and memory usage:
- pm2 monit
Now that your Node.js application is running, and managed by PM2, let’s set up the reverse proxy.
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 an Nginx web server as a reverse proxy for this purpose. This tutorial will set up an Nginx server from scratch. If you already have an Nginx server setup, you can just copy the location
block into the server block of your choice (make sure the location does not conflict with any of your web server’s existing content).
First, install Nginx using apt-get:
- sudo apt-get install nginx
Now open the default server block configuration file for editing:
- sudo nano /etc/nginx/sites-available/default
Delete everything in the file and insert the following configuration. Be sure to substitute your own domain name for the server_name
directive. Additionally, change the port (8080
) if your application is set to listen on a different port:
server {
listen 80;
server_name example.com;
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 http://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
:
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:
- sudo nginx -t
Next, restart Nginx:
- sudo systemctl restart nginx
Next, permit traffic to Nginx through a firewall, if you have it enabled.
If you’re using ufw, you can use the following command:
- sudo ufw allow 'Nginx Full'
With ufw, you can always check the status with the following command:
- sudo ufw status
If you’re using IPTables instead, you can permit traffic to Nginx by using the following command:
- sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT
You can always check the status of your IPTables by running the following command:
- sudo iptables -S
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).
From here, you should continue to secure your set up by reading How To Secure Nginx with Let’s Encrypt on Debian 8.
Congratulations! You now have your Node.js application running behind an Nginx reverse proxy on a Debian 8 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!
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
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!
Hi @ltagliaferri ,
So, it turns out that my issue had a very simple solution - when I pulled my app over from github to my sub domain root, I forgot to
npm install
my dependencies :(:(. This is definitely an epic fail on my part, and hopefully a lesson to future readers.One gotcha to note, however, is that I was further confused because after I got both apps working (the sub domain worked just fine after I installed everything) running
systemctl status pm2
after adding the systemd unit file per the tutorial produced the following result:Which made me think that the unit and service were failing to be configured correctly. It turns out that running the script created the service under my user name, at the following location:
/etc/systemd/system/pm2-blakers757.service
.Just something to note if you’re troubleshooting this in the future.
Thanks!
There appears to be a missing steps, a “pm2 save” command is needed to save the .pm2/dump.pm2 file which is used when your server is rebooted to restart services.
@manningkathryn @ltagliaferri Kathryn I’m honestly not sure, unfortunately, perhaps @ltagliaferri can give you more guidance. I would think you could modify this command:
to create the systemd unit under
/etc/systemd/system/pm2.service
instead of/etc/systemd/system/your-user-name.service
, but that’s what gave me trouble. If I were you, I’d just let it create the service under your user and use that. Otherwise, the pm2 docs might be able to help.Hi @ltagliaferri ,
unfortunately I’m still not able to get my sub domain up and running. I did file a support ticket but they haven’t been able to do much to help me so far.
I’m getting a 502 error when I try to visit the site in a browser, and testing the port I’m trying to run the app on in localhost with cURL gives me an error:
while my primary domain works fine and returns html. I’m actually starting to wonder if the problem could be with my pm2 config - I’m able to add my primary app’s Node server file and start it up and everything looks good, but when I try to add my other app’s file and then use
pm2 list
it has errored (luckily the other app continues to run fine though.)I thought that I went through a configuration step for pm2 the first time through the tutorial, and I"m wondering if something needs to be changed in there so pm2 knows what settings to use for the sub domain. Can you point me to where I might find the config file? I looked at the pm2 docs but they don’t make it easy to figure out where this might be in our case.
Thanks!
Hi @ltagliaferri ,
I finally got my letsencrypt cert covering all domains (blakehschwartz.com, portfolio.blakehschwartz.com & www versions of each). I followed the instructions given in let’s encrypt and others, and finally had to fall back to using nodejs app on ubuntu and following the rest of the other tutorial to get the additional security in place (the Webroot plugin failed for me every time, I had to use the Standalone plugin to generate the cert). Now, unfortunately, my top-level domain blakehschwartz.com is accessible and using https but portfolio.blakehschwartz.com is returning a 502 - Bad Gateway error. At this point I just need to get these to work so I can move on, should I file a support request or is there any additional trouble shooting you can help with?
Thanks
Hello,
I followed the same tutorial for Ubuntu 16.04, I’m posting here because that thread doesn’t seem to be monitored anymore and the steps are the same. I have everything up and running (getting my hello world results, and https is working) I can’t figure out how the file structure is supposed to work with this setup on my server. I’m used to putting everything in /var/www/html, but with the reverse proxy setup (which I’m new to) it seems like that is somewhat different. Also, I’ve never seen the use of
#!/usr/bin/env nodejs
in a javascript/node file before. Is there a resource you could point to for learning about file structure in this kind of environment?Also, I’d like to add a sub-domain to my nginx setup (I already have it set up through the DO dashboard and pointing to my public ip address) - but again I can’t figure out how this is supposed to look with the reverse proxy setup. I understand that I could add another location block to point to
example.com/app2
, but what if I want to useportfolio.example.com
, can it still listen on port 80/443 and proxy to 8081 (since example.com is running on 8080)? What wouldlocation / {
look like in that case?Any help would be great! thanks!