Run your Node.js application on a headless Raspberry Pi
Bogdan Covrig
Posted on February 17, 2019
Recently I've got a little piece of hardware (Raspberry Pi 2 Model B) on my desk. Rather than have it sit on its ass all day, I got a little Node.js application up and running through Nginx.
Get that Raspberry Pi up and running
A headless install doesn't require any kind of extra hardware (such as screens or keyboard), so everything that you need is the Raspberry Pi itself, a microSD (or SD, depending on your hardware) card and an internet connection (wired or wireless, again depending on your Raspberry).
Raspbian is the most optimized OS for Raspberries and I use it when I need a minimum and fast setup. Just go the official website and download the latest version of Raspbian Stretch Lite.
2. Burn that image
Insert your card in your PC and burn the Raspbian image on it.
I followed these instructions (Linux/Windows also available) because I prefer using my terminal, but Etcher (a graphical alternative on steroids) is also available on all platforms.
3. Enable headless SSH connections
SSH is not enabled by default in Raspbian, so you will have to do it before you boot the card for the first time.
After the installation, go to boot/ and create a file called ssh (no extension).
touch ssh
4. Boot that Raspberry Pi
Insert the card, the power source, and the network cable. After the green LEDs stop blinking, your Raspberry Pi is good to go!
5. Find that Raspberry Pi
So you have power, network and an OS, no keyboard, no screen. How do you connect to the Raspberry Pi?
In order to SSH into it, you will have to find it in your network first. Supposing that you connected the Raspberry to your home network, you can see all the devices (MAC and IP addresses) in your LAN from the ARP table. Simply run in your terminal (working on all platforms)
arp -a
and you will find your Raspberry Pi right there.
fritz.box (192.168.178.1) on en1 ifscope [ethernet]
raspberrypi.fritz.box (192.168.178.73) on en1 ifscope [ethernet]
In my case, fritz.box is my router and right after is my Raspberry Pi. So from now on, I will connect to it through 192.168.178.73 address.
More about ARP tables and how you should find your devices there.
This repository contains the instructions to install the NodeSource N|solid and Node.js Binary Distributions via .rpm and .deb as well as their setup and support scripts.
If you're looking for more information on NodeSource's low-impact Node.js performance monitoring platform, Learn more here.
New Update ⚠️
We'd like to inform you of important changes to our distribution repository nodesource/distributions.
What's New:
Package Changes: DEB and RPM packages are now available under the nodistro codename. We no longer package the installer coupled to specific versions. This means you can install Node.js on almost any distro that meets the minimum requirements.
Installation Scripts: Back by popular demand, the installation scripts have returned and are better than ever. See the installation instructions below for details on how to use them.
RPM Package Signing Key: The key used to sign RPM packages has changed. We now sign…
If you choose to write or paste the code, quickly install vim, it will make our lives easier and later I will walk you through, don't worry.
sudo apt-get update
sudo apt-get install vim -y
2. Get your Node.js app
Write, copy-paste or clone the Node.js application. For testing purposes, I created app.js file.
cd ~
vim app.js
I pasted the following boilerplate
consthttp=require('http');constPORT=3000;constserver=http.createServer((req,res)=>{res.statusCode=200;res.setHeader('Content-Type','text/plain');res.end('Hello dev.to!\n');});server.listen(PORT,()=>{console.log(`Server running on port ${PORT}.`);});
If vim is too overwhelming you can try to use other ways as nano. But just to be sure, this is a really quick follow-up:
Open (or create) the file with vim app.js.
Now, vim is in the normal mode and it's waiting for your command. Press i to go in the insert mode, this will allow you write code.
Type or paste your code now, exactly how you'd do it in your editor.
If you're done writing, press esc so you go back to the normal mode so you can command vim to save and exit.
Usually, vim commands start with :. Gently press : followed by w for writing and q for quitting. You can actually see the commands that you're typing on the bottom of your terminal. Press enter to acknowledge the commands.
Taa-daaaaa. app.js is saved.
If you want to do more crazy tricks with vim, follow-up this beginner guide and you will see that vim is not that bad.
$ node app
Server running at 127.0.0.1 on port 3000.
and your Node.js app will run on localhost:3000/.
Because none of the ports are opened by default, you can test the app only from your Raspberry Pi. Open a new tab of the SSH connection and run
curl localhost:3000
and you should get
Hello dev.to!
4. Install PM2
Of course that you want your application daemonized (in background) and of course that you want your application to start when the system is restarting. PM2 will provide all of this.
Stop your Node.js application (ctrl + C) and proceed to installation.
We will use npm to install PM2 globally -g.
sudo npm install-g pm2
Start the application with PM2
To start app.js with PM2 run
bash
pm2 start app.js
and you should see
[PM2] Starting /home/pi/app.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬─────────┬──────┬─────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
├──────────┼────┼─────────┼──────┼─────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ app │ 0 │ N/A │ fork │ 738 │ online │ 0 │ 0s │ 0% │ 21.8 MB │ pi │ disabled │
└──────────┴────┴─────────┴──────┴─────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
Now app.js is daemonized running. You can test it as we did before with curl localhost:3000.
Bonus: if the app crashes, PM2 will restart it.
PM2 startup
The pm2 startup command will generate a script that will lunch PM2 on boot together with the applications that you configure to start.
pm2 startup systemd
will generate
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u pi --hp /home/pi
Copy the generated command and run it.
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u pi --hp /home/p
This created a system unit that will start PM2 on boot. When the system will boot PM2 will resurrect from a dump file that is not created yet. To create it run
pm2 save
This will save the current state of PM2 (with app.js running) in a dump file that will be used when resurrecting PM2.
That's it! Your application is currently running and in case of a restart, it will start when the system boots.
PM2 daemon
You will be able to check anytime the status of your application with pm2 list, pm2 status or pm2 show.
$ pm2 list
┌──────────┬────┬─────────┬──────┬─────┬────────┬─────────┬────────┬──────┬───────────┬──────┬──────────┐
│ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
├──────────┼────┼─────────┼──────┼─────┼────────┼─────────┼────────┼──────┼───────────┼──────┼──────────┤
│ app │ 0 │ N/A │ fork │ 451 │ online │ 0 │ 96m │ 0.2% │ 31.8 MB │ pi │ disabled │
└──────────┴────┴─────────┴──────┴─────┴────────┴─────────┴────────┴──────┴───────────┴──────┴──────────┘
As I mentioned before, none of the ports on your devices are publicly open yet, so you cannot access your Raspberry Pi from the outer world. There are a ton of reason why you should or shouldn't use a reverse proxy for your Node.js application. Because of scalability and security reasons (and also is really simple to setup and manage), I will use Nginx as a Reverse Proxy Server for this application.
0. Don't use a Reverse Proxy :(
If you plan to use a Reverse Proxy don't follow this step otherwise you will mess up the ports (like having 80 and 3000 opened at the same time).
An uncomplicated way to go without a Reverse Proxy is to use ufw to allow some of the ports to allow incoming traffic. But note that this might be a big security flaw.
Install it by running
sudo apt-get install ufw
A quick sudo ufw status verbose will show us that ufw is currently inactive. Before you enable it, you should allow all the SSH traffic to your device, so the connection is not disturbed.
Another quick sudo ufw status verbose will show that all incoming SSH traffic is allowed. All the outgoing traffic is allowed, so don't worry about it. Now just go on and allow connections on 3000, the port of your application.
sudo ufw allow 3000
Now you can access from the outside of the world! You can type your device's address followed by the port in your browser.
1. Install NGINX
I used Nginx as a Reverse Proxy Server to redirect all the traffic to/from port 80 to my application, on port 3000. Install Nginx running
sudo apt update
sudo apt install nginx
After the installation, Nginx will be running right away. The default port opened is 80 and you can test it by browsing to your Raspberry's address.
2. Configure the Reverse Proxy Server
There is a lot to say about Reverse Proxies, but we will stick to basics now.
You will edit the default configuration (that serves the HTML page that you saw in your browser) to make the proper redirects.
sudo vim /etc/nginx/sites-available/default
If you are not familiar to Nginx, /etc/nginx/sites-available/default is a long, confusing file. I will get rid of all the comments so you can see it better.
server{listen80default_server;listen[::]:80default_server;root/var/www/html;indexindex.htmlindex.htmindex.nginx-debian.html;server_name_;location/{# First attempt to serve request as file, then# as directory, then fall back to displaying a 404.try_files$uri$uri/=404;# 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;}}
You will need the basic configuration, therefore leave it be. You will make changes to location / { block.
Uncomment the commented section inside that block, change the port to 3000, get rid of the first lines and that exact configuration is a Reverse Proxy (or just copy the following code).
Now you are done! So you got a Node.js application running (daemonized) on a headless Raspberry Pi that deals with requests through an Nginx Reverse Proxy Server.
Hopefully, this was a comprehensive enough guide, but I am open to discussions and questions below. Let us know what you've experienced or what other alternatives you found on the way.