DEV Community

Cover image for How to run a Node.js server with Nginx
Matt Angelosanto for LogRocket

Posted on • Originally published at blog.logrocket.com

How to run a Node.js server with Nginx

Written by Ikeh Akinyemi ✏️

Node.js is a vital tool for almost all kinds of microservices‑based development and delivery. It’s also the leading tool for creating server applications in JavaScript and offering the functionality of both a web server and an application server.

But Node.js has some shortcomings and vulnerabilities that can cause unsatisfactory performance or even crashes within Node-based applications. For example, Node.js‑based web applications are prone to slow code execution or even crashes due to IO-bound operations or rapid traffic growth. They also sometimes struggle with serving static content such as images and JavaScript files and load balancing across multiple servers.

Fortunately, you can cache static content, proxy and load balance among multiple application servers, and manage port contention between clients using Nginx. This makes Nginx an excellent tool for increasing Node.js performance.

In this tutorial, we'll show you how to run a server with Nginx. We'll cover the installation and configuration of Nginx by building a simple Node.js application.

To follow along, you should have:

  • An understanding of how the web, web servers, and web browsers communicate
  • Basic knowledge of JavaScript, Node.js, and asynchronous programming
  • Node.js installed on your local machine
  • Vim text editor installed on your local device

To demonstrate how Nginx works in practice, we'll build a simple Node.js application that will run on port 3000, which is a common port for Node apps to run on. The same techniques should work for any other port that's available on your local machine.

What are servers and web servers?

A server is a computer that communicates with other computers to serve them with the information requested by these computers. These computers, also called clients, connect to a server through either a local area network (LAN) or a wide area network (WAN). A server sends and collects information across a network within multiple locations.

The server on the web, known as a web server, through the Hypertext Transfer Protocol (HTTP), receives requests from a client via the internet, such as the browser, and returns an HTTP response, either as an HTML webpage or in a JSON format as with API calls.

Web servers are the foundation of any data exchange using HTTP as its client-server protocol for communication with other computers. A web server as a computer involves both hardware and software and is a fundamental component in web development. The software side makes up the parts that understand URLs and control how web users access hosted files.

What is Nginx?

According to the documentation, Nginx (pronounced “engine X”) is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev.

Nginx is used for a variety of tasks that contribute to improving Node.js performance. Key features include:

  • Reverse proxy server — As traffic to your app increases, the best approach to improve performance is to use Nginx as a reverse proxy server in front of the Node.js server to load balance traffic across the servers. This is the core use case of Nginx in Node.js applications
  • Stateless load balancing — This improves performance while reducing load on backend services by sending off client requests to be fulfilled by any server with access to the requested file
  • Cache static contents — Serving static content in a Node.js application and using Nginx as a reverse proxy server doubles the application performance to a maximum of 1,600 requests per second
  • Implement SSL/TLS and HTTP/2 — Given the recent shift from using SSL/TLS to secure user interactions in Node.js applications, Nginx also supports HTTP/2 connections
  • Performance tracking — You can keep real-time tabs on the overall performance of your Node.js application using the stats provided on Nginx’s live dashboards
  • Scalability — Depending on what assets you’re serving, you can take advantage of the full‑featured HTTP, TCP, and UDP load balancing in Nginx to scale up your Node.js application

Nginx currently supports seven scripting languages: Go, Node.js, Perl, PHP, Python, Ruby, and Java Servlet Containers (the last is an experimental module). It enables you to run applications written in different languages on the same server.

Installing Nginx

We'll install the Nginx using the default package manager for a Debian-based operating system, which is called apt. Nginx is also available for almost all operating systems under their default repositories.

Before installing Nginx, make sure you’ve installed the prerequisites for Ubuntu OS.

Later in the tutorial, we'll configure Nginx based on the unique needs of our project, then we'll be ready to implement it. Now let's install Nginx using apt.



sudo apt update
sudo apt install nginx


Enter fullscreen mode Exit fullscreen mode

With a successful installation, the following message should be output on the terminal confirming that Nginx has been installed:



Thanks for using nginx!

Please find the official documentation for nginx here:
* https://nginx.org/en/docs/


Enter fullscreen mode Exit fullscreen mode

Before configuring Nginx, let's quickly set up and build our Node.js application.

Creating a Node.js application

For this simple Node.js application, we'll be building a Node.js server with the HTTP module provided by Node.js. Let's started by creating a folder and initializing the project on the terminal:



mkdir 'nginX server project'
cd 'nginX server project'
npm init -y


Enter fullscreen mode Exit fullscreen mode

The above code will create the folder nginX server project and change the directory into the folder. We then initialize a Node.js application with npm, using the -y flag to set “yes” as the default answer to all the questions.

The next step is to create the server.js file that contains the source code for our application and open it up with any IDE or text editor of your choice:



touch server.js
vim server.js
#or 
code .


Enter fullscreen mode Exit fullscreen mode

Now it’s time to build and start the server. Let’s define two extra subdomains as a means to test that our application is fully functional:



const http = require("http");

const server = http.createServer((req, res) => {
  const urlPath = req.url;
  if (urlPath === "/overview") {
    res.end('Welcome to the "overview page" of the nginX project');
  } else if (urlPath === "/api") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(
      JSON.stringify({
        product_id: "xyz12u3",
        product_name: "NginX injector",
      })
    );
  } else {
    res.end("Successfully started a server");
  }
});

server.listen(3000, "localhost", () => {
  console.log("Listening for request");
});


Enter fullscreen mode Exit fullscreen mode

We created a server with a Node.js HTTP module that we imported using the require function in the above code. Within our server, we’ll render two different responses, depending on our current route. The two routes are /overview and /api.

On the /overview subdomain, we’ll render a plain text, while on the /api we’ll render a JSON object. Our default domain with the address 127.0.0.1:3000, coupled with the two routes we created, will help us further understand how Nginx works.

Let’s configure Nginx to test run our server from the terminal.

Configuring Nginx

At the end of the installation process we walked through earlier, Ubuntu 20.04 started Nginx. The server should already be up and running.

Open up your browser and navigate to your server’s IP address, where you can access the default Nginx landing page to confirm that the software is running properly. Welcome to Nginx Page

For Nginx to listen for port 3000 from our Node.js application, we’ll change the directory to /etc/nginx/sites-available, where we’ll create a server block to contain configuration with the correct directives pointing to port 3000:



cd /etc/nginx/sites-available
sudo cp default myserver 


Enter fullscreen mode Exit fullscreen mode

After changing the directory to /etc/nginx/sites-available, the second command will copy and paste the content of the default Nginx configurations into a new file named myserver.

Next, open the file and add the appropriate directives to port 3000:



sudo vim /etc/nginx/sites-available/myserver


Enter fullscreen mode Exit fullscreen mode

Paste in the following configuration block, which is similar to the default, but updated for port 3000:



#The Nginx server instance
server {
    listen 0.0.0.0:80;
    server_name localhost;

    location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;

      proxy_pass http://127.0.0.1:3000/;
      proxy_redirect off;
    }

    location /overview {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;

      proxy_pass http://127.0.0.1:3000/overview;
      proxy_redirect off;
    }

    location /api {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;

      proxy_pass http://127.0.0.1:3000/api;
      proxy_redirect off;
    }
 }


Enter fullscreen mode Exit fullscreen mode

Save the changes and exit the file. Notice that we’ve created three more locations in the above configuration file, matching the predefined paths in our Node.js application.

For the next step, let’s enable the above file by creating a link from it to the sites-enabled directory, which Nginx reads from during startup:



sudo ln -s /etc/nginx/sites-available/myserver /etc/nginx/sites-enabled/


Enter fullscreen mode Exit fullscreen mode

The server block is now enabled and configured to return responses to requests based on the listen port and current port directives.

Now it’s time to start both our Node.js application and the Nginx service to effect the recent changes. But first, let’s check the status of Nginx to confirm that the configuration is working properly:



sudo nginx -t 


Enter fullscreen mode Exit fullscreen mode

The output upon running the above command would look like this:



nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful


Enter fullscreen mode Exit fullscreen mode

The above output confirms that our configuration was successful. Next, stop and restart Nginx to enable your changes. Nginx is started upon installation by default.



sudo systemctl restart nginx


Enter fullscreen mode Exit fullscreen mode

If Nginx doesn’t automatically start for some reason, the command to start it is:



sudo systemctl start nginx


Enter fullscreen mode Exit fullscreen mode

Open a new terminal and change directory to the Node.js application. Now start your application using the following command:



node server.js


Enter fullscreen mode Exit fullscreen mode

Open your browser and access the Node.js application. Nginx should currently serve the application ports that were defined in the configuration file. You can test this by navigating to localhost/overview: Overview Page for Nginx Project

For the default port 127.0.0.1 to work for our Node.js application, we need to edit the default configuration of Nginx to point to another port. We’ll use the port 127.0.0.1:8080. Change the directory to /etc/nginx/sites-available/ and edit the default file as follows:



...
  server {
      listen 8080 default_server;
      listen [::]:8080 default_server;
      ...
  }
...


Enter fullscreen mode Exit fullscreen mode

Now we can navigate to the address 127.0.0.1 on the browser and access the home port :3000 of our application: Started a Server Page

To further test that every other path we defined is working, let’s try the last path, /api: Testing Last Path

Conclusion

In this tutorial, we learned how to set up Nginx as a Node.js server for backend applications.

We built a simple Node.js application using Node.js and Nginx. Then, we configured Nginx to listen for port 3000 and serve the contents we have predefined within our Node.js application on the browser.

Ngnix is a great tool for web application delivery, providing performance and scalability at both the ADC and web server layers.


200’s only ✔️ Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket.

LogRocket Network Request Monitoring

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.

Top comments (3)

Collapse
 
prodanov17 profile image
prodanov17

If you're logged into the server using SSH, how do you keep the Node.js app running when you exit SSH? I'm using Linode for my example.

Collapse
 
exec profile image
exec • Edited

If you're just testing out your app, the screen utility can be a straightforward and effective choice. screen is a terminal multiplexer that lets you start, detach from, and reattach to sessions. This makes it ideal when you're writing code and testing your app—you can keep it running in a detached screen session while you attend to other tasks, and reattach whenever you're ready. It offers a good blend of simplicity and utility that fits well with the demands of development work. You can find out more about screen here.

However, when your application is ready for deployment or production use, you might want something that can start your app on boot, restart on crash automatically (without weird shell script loops), etc. That's where systemd comes in! (provided you're on a Linux distribution that uses it as the default init system) systemd allows you to configure your Node.js application as a service, enabling it to start at boot time or upon manual start. More crucially, systemd provides the ability to automatically restart your application if it were to crash, thereby maintaining uptime and providing a level of robustness required in production environments.

If you're running a Linux distribution that doesn't use systemd or if you just prefer a different solution, process managers such as pm2 or forever can be viable alternatives!

Hope this helps!

Collapse
 
maro01 profile image
Maro

I use PM2, it allows you to run your code and restarts your application if it crashes