DEV Community

Cover image for How To Serve Node.js Applications with Nginx on a Raspberry
Guim
Guim

Posted on • Updated on

How To Serve Node.js Applications with Nginx on a Raspberry

This is a tutorial of how I made my own server with Nginx to serve Node.js applications using a Raspberry Pi on my local network. I did it to have my own development server in my local network before committing any changes to the production host. We’ll go through all the steps so you can easily make your own too.

Prerequisites and specifications

For this tutorial, I suppose you have a Rasberry Pi with the Raspbian OS installed, root access to the board, another computer connected on the same network, and access to your Wifi app.

  • Board model: Raspberry Pi 3 B+
  • Board OS: Raspbian
  • Board CPU: ARMv7l

Install and run Nginx

To install Nginx and start the service on your Raspberry, open the terminal and type the following commands:

sudo apt-get update
sudo apt-get install nginx
sudo systemctl start nginx
Enter fullscreen mode Exit fullscreen mode

Now your Nginx server is up and running. To check if the server is working properly, we can open a web browser from another computer and search for our Raspberry local IP. If you don’t know whats your Raspberry local IP open a new terminal and type:

hostname -I
Enter fullscreen mode Exit fullscreen mode

In my case, it’s 192.168.0.173. So the address I have to search for will be http://192.168.0.173. Here you can see the default landing page of Nginx.

Nginx default page

Congratulations, your Nginx server is working. Now we have to serve Node.js applications instead of static HTML files. How?

Install Node.js and NPM

Obviously, to work with Node.js, we first need to install Node.js packages and NPM (node package manager). We’ll also update NPM to the latest version. To do so, open a new terminal and type:

sudo apt-get install nodejs
sudo apt-get install npm
npm install -g npm
Enter fullscreen mode Exit fullscreen mode

To check if both Node.js and NPM are installed properly, run the following commands:

nodejs -v
npm -v
Enter fullscreen mode Exit fullscreen mode

Now that we have Nginx running and Node.js installed, it’s time to make our first Node app. The app will be a simple "Hello World" to check if Node and Nginx have good communication. Let’s write the app.

Create a Node.js app

In your home directory create a new folder called server. In this folder, we’ll create our Node.js app. Let’s write the index.js file.

const express = require("express");
const app = express();

// The HelloWorld
app.get("/", (req, res) => {
  res.send("Hello from Node.js!");
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`App listening on http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Save this file in the server directory we created before. To make this app work, we need to install the express packages. Open a terminal, go to your server folder and use NPM to install them:

cd ~/server
npm init -y
npm install express --save
Enter fullscreen mode Exit fullscreen mode

What we did is to initialize NPM and install the express packages. Now we can run the app with Node.js:

nodejs index.js
Enter fullscreen mode Exit fullscreen mode

If you open a browser in your Raspberry and search for http://localhost:3000 you’ll see this Node "Hello World".

Nginx Proxy to the app

Our app is now running in port 3000. But what we want is to redirect all the HTTP requests in port 80 (the Nginx server) to the port 3000 (the Node.js app). We can do this modifying the default configuration file of Nginx.

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;

        server_name _;

        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;
        }
}
Enter fullscreen mode Exit fullscreen mode

Copy this configuration, delete all the text in your config file and paste it. To edit your default file type:

sudo nano /etc/nginx/sites-enabled/default
Enter fullscreen mode Exit fullscreen mode

When you’re finished editing type crtl+X to save changes before exit. Don’t forget to restart your Nginx service:

sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

Now your Nginx is redirecting your HTTP requests to your localhost app. If we visit http://192.168.0.173 again (the address where Nginx is listening to) we now have to see the Node “Hello World”.

Node app served with nginx

Configure IP tables

The only problem right now is we can access the app via http://localhost (thanks to Nginx) but still with http://localhost:3000 (the Node.js direct access). We don’t want this port 3000 open to anyone.

Imagine in the future you want to redirect all port 80 connections to port 443 because you transport personal data and you need an SSL certificate. People could still connect to port 3000 an omit this encrypted connection.

To solve this we just need to change two rules in our IP tables. The first one will allow localhost (the Raspberry) to connect to port 3000. The second one will deny access to anyone else. Open the terminal and type this:

iptables -A INPUT -p tcp -s localhost --dport 3000 -j ACCEPT
iptables -A INPUT -p tcp --dport 3000 -j DROP
Enter fullscreen mode Exit fullscreen mode

DHCP IP reservation

Another thing you may want to do is to reserve an IP for your Raspberry. So your device has the same IP address every time it connects to your Wifi.

  • Open your Wifi app visiting: http://192.168.0.1 (This is your routers address)
  • Log in with the user and password printed on the bottom of the router.
  • There is a section called Advanced Networking.
  • Search for the DHCP IP reservations.
  • Click add device and search for your Raspberry.
  • Change the last number of the IP address to your will.

Last words

I hope this tutorial was useful for you. If you had any trouble in any of the steps just leave it in the comments below and I’ll do my best to help you. Thanks!

Top comments (9)

Collapse
 
timmachinelogic profile image
tim-machine-logic

First, this was super helpful so thank you. I'm stuck on the following command:

iptables -A INPUT -p tcp -s localhost --dport 3000 -j ACCEPT

I get the following error:

iptables v1.8.2 (nf_tables): unknown option "--dport"

I tried going to the iptables documentation and they have similar commands like the following so not sure why the first one doesn't work:

iptables -A INPUT -p tcp -s 15.15.15.0/24 --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

Collapse
 
jxboi profile image
Jia Xing

iptables-legacy -A INPUT -p tcp -s localhost --dport 3000 -j ACCEPT
iptables-legacy -A INPUT -p tcp --dport 3000 -j DROP

this works for me

Collapse
 
zeerorg profile image
Rishabh Gupta

You can use ufw package to block incoming traffic at a port, then you won't have to work with iptables

Collapse
 
guimg profile image
Guim

Indeed, but I'm used to doing it from the iptables software itself. 😄

Collapse
 
brandonwallace profile image
brandon_wallace

If you have a Raspberry Pi running as a web server you could set the IP address statically. It will work even if you have a DHCP server running on the network. If the DHCP server stops you will not be able to get to your web server.

Collapse
 
nahinakbar profile image
Nahin Akbar

Will this work on a VPS?

Collapse
 
guimg profile image
Guim

Sure, I have a VPS hosting for my personal website and I follow the same logic explained here (without the Raspberry of course). Worked perfectly for me.

Collapse
 
luiscarlosb3 profile image
Luis Carlos Galvão de Oliveira

I tried to load a ejs page with a public default folder and any of my css file was found, anyone have any tip for this?

Collapse
 
recurs1v0 profile image
EPPR

You need to add this on line 3

app.use(express.static('public'))

This is how you specify that a folder called "public" will serve static files.

Assuming you have a folder called "css" inside "public"
you can then access like
raspberrypi.local/css/styles.css