DEV Community

Cover image for How to deploy a Node/React App on a Linux server
Aduramimo Oludare
Aduramimo Oludare

Posted on • Updated on

How to deploy a Node/React App on a Linux server

When I started learning Node JS few weeks ago, the very first question that came to my mind was: hosting and deployment.

There are several solutions online (Netlify, Heroku etc) but this is for developers who will at some point find themselves in situations where they have to deploy on managed servers.

So let's dive in with the following steps:
1.Provision a Linux Ubuntu server. Check out how to get one from Azure in this article

2.SSH into the machine and install node and npm globally:

sudo apt update

curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -

sudo apt install nodejs
Enter fullscreen mode Exit fullscreen mode

Verify your node and npm installations:

node -v

npm -v
Enter fullscreen mode Exit fullscreen mode

3.cd into /var/www/html, create a folder where your node files will sit:

sudo mkdir node_project
Enter fullscreen mode Exit fullscreen mode

Ensure to grant your user ownership over the folder so you can be able to make file edits:

sudo chown -R $USER:www-data node_project
sudo chmod -R 775 node_project
Enter fullscreen mode Exit fullscreen mode

Create a blank index.js file, which will be the entry point of the application.

sudo nano index.js
Enter fullscreen mode Exit fullscreen mode

4.Initialize and install the various dependencies needed for a Node project to run, i.e. the node_modules folder:

npm init
Enter fullscreen mode Exit fullscreen mode

The wizard will bring up several questions in the terminal, fill in some defaults and press the space key for the ones you have no answer for:

Package.json init
Type ok to confirm.

5.Edit the index.js file and bring in some quick-fire essential code to start up our node server:

index.js

6.Install dependencies needed in the node_modules folder. We only need express installed for this tutorial:

npm install express
Enter fullscreen mode Exit fullscreen mode

Test your project and see if its running:

node index.js
Enter fullscreen mode Exit fullscreen mode

node server running

7.The server is now running on the port specified (4000), once you go to http://(your-domain):4000 you should see the output. However, this is only good enough for development/staging. We do not want users having to access our application and having to type in port names, for SEO purposes and for the sake of branding. The solution is to make the Apache web server serve as reverse-proxy for the application so we would be able to type http://(your-domain) and still get to the node application.

Create a new virtual host file for our new app (Further reading: Host several websites on a Linux server using Virtual Hosts)

sudo nano /etc/apache2/sites-available/node-project.conf
Enter fullscreen mode Exit fullscreen mode

Add the following lines:

<VirtualHost *:80>
    ServerName www.node-project.com
    DocumentRoot /var/www/html/node_project

    ProxyRequests Off
    ProxyPreserveHost On
    ProxyVia Full

    <Proxy *>
        Require all granted
    </Proxy>

    ProxyPass / http://localhost:4000/
    ProxyPassReverse / http://localhost:4000/
</VirtualHost>
Enter fullscreen mode Exit fullscreen mode

Save and close the file. Enable the config file:

sudo a2ensite node-project.conf
Enter fullscreen mode Exit fullscreen mode

Enable proxy modules:

sudo a2enmod proxy proxy_http rewrite headers expires
Enter fullscreen mode Exit fullscreen mode

Disable the default virtual host config file:

sudo a2dissite 000-default
Enter fullscreen mode Exit fullscreen mode

Restart Apache:

sudo service apache2 restart
Enter fullscreen mode Exit fullscreen mode

Navigating to your website will bring you to your Node app.

8.Node applications typically shut down after you manually stop the server, after SSH connection time-out or server restarts. Prevent this by making it a default start-up service to always start up in the event of any of the above-mentioned factors.

Create a systemd service file by the following command:

sudo nano /lib/systemd/system/node-project.service
Enter fullscreen mode Exit fullscreen mode

Copy and paste the following code:

[Unit]
Description=Node.js Application
After=syslog.target network.target

[Service]
Type=simple
User=Aduramimo
WorkingDirectory=/var/www/html/node_project
Environment=NODE_ENV=production
ExecStart=/usr/bin/node index.js

Restart=always

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

The After keyword signifies that this service should fire after essential Linux kernel files have loaded properly, User will be your username to the linux server. We do not use root for security reasons.
WorkingDirectory is the absolute path to our project folder, Environment signifies where in the app the service should target (this is also the same value in your NODE_ENV variable in the .env file), while ExecStart is the actual process to be fired. We call the node daemon where it is been installed on the entry point already defined in step 4 above.

Save and exit.

9.Run the following code in turn:

sudo systemctl daemon-reload
sudo systemctl start node-project
sudo systemctl enable node-project
sudo systemctl status node-project
Enter fullscreen mode Exit fullscreen mode

You should get the following interface which means that the service is running successfully:
Node service

Please note that this will assume your default Node Terminal from now on. Any changes made to your server will be effected by the commands shown above and status also shown there.

Automatically refresh your Node server to watch for file changes in your entry point (index.js) by installing Nodemon with

npm install nodemon
Enter fullscreen mode Exit fullscreen mode


in your project directory and replace /usr/bin/node with /usr/bin/nodemon in the ExecStart command of the service created.

10.We are done deploying the Node application. Some Linux distros however, like CentOS or Redhat, might prevent the service file running due to some security feature known as SELinux (Security-Enhanced Linux) that is enabled by default. If this applies to you, follow the following steps to troubleshoot and resolve:

Check the status of SELinux :

sudo sestatus
Enter fullscreen mode Exit fullscreen mode

It will be something like this:

SELinux status

Now we need to temporarily disable it by setting the status to permissive. This is like disabling SELinux but also logs critical error messages for us to troubleshoot the root cause.

sudo nano /etc/sysconfig/selinux
Enter fullscreen mode Exit fullscreen mode

SELinux

Changed enforcing to permissive.

Restart the server:

 sudo reboot
Enter fullscreen mode Exit fullscreen mode


and then check the status:

 sudo sestatus
Enter fullscreen mode Exit fullscreen mode


It should be on permissive.

The Node service file will be running now, however, our work is not done, since it is strongly advised not to turn off SELinux or stay on permissive mode.

Look out for the most recent error messages logged by the OS via

sudo nano /var/log/messages
Enter fullscreen mode Exit fullscreen mode


you should see something of the following nature in the console:

Oct  5 09:07:34 Aduramimo setroubleshoot[1811]: SELinux is preventing 
/usr/bin/node from using the execmem access on a process. 

For complete SELinux messages run: sealert -l e5cb75a6-c29c-4461-b716-d1a376e$
Enter fullscreen mode Exit fullscreen mode

Follow the recommendation in the result. Note that you might get a query error of ID not found if you try to run the sealert command above. To get the full ID, run

sudo sealert -l '*' | grep <id_from messages>
Enter fullscreen mode Exit fullscreen mode

Follow the action point suggested. A typical fix will be something like:

## sudo ausearch -c 'npm' --raw | audit2allow -M my-npm
## sudo semodule -X 300 -i my-npm.pp
Enter fullscreen mode Exit fullscreen mode

We will now re-enable selinux to the enforced state by editing the config file and changing permissive to enforcing and reboot the server.

sudo nano /etc/sysconfig/selinux

sudo reboot
Enter fullscreen mode Exit fullscreen mode

To deploy a React project:
The above steps applies for react applications, except for step 7, where we change the React service file slightly:

[Unit]
Description=React.js Application
After=syslog.target network.target

[Service]
Type=simple
User=Aduramimo
WorkingDirectory=/var/www/html/react_project
Environment=NODE_ENV=production
ExecStart=/usr/bin/npm start

Restart=always

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

The major difference is in the ExecStart command which we adjust to reflect how a react app normally starts up.

If you installed Node and NPM via NVM on your server, the above might not work. You might try look at the following alternative steps if the React app fails to startup:

[Unit]
Description=React.js Application
After=syslog.target network.target

[Service]
Type=simple
User=Aduramimo
WorkingDirectory=/var/www/html/react_project
Environment=NODE_ENV=production
ExecStart=/home/{USER}/react.sh

Restart=always

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Where {USER} is your Linux server.

Navigate to your user's root directory and follow the following:

sudo nano react.sh
Enter fullscreen mode Exit fullscreen mode

Copy the following code:

#!/bin/bash
. /home/{USER}/.nvm/nvm.sh
npm start
Enter fullscreen mode Exit fullscreen mode

Then finally

chmod +x /home/{USER}/react.sh

Restart the systemd daemon and start the service.

You can also try the following method:

Replace the value of ExecStart in the systemd service file to

/home/{USER}/.nvm/nvm-exec npm start

For a Node application, Replace the value of ExecStart in the systemd service file to

/home/{USER}/.nvm/nvm-exec node /var/www/html/index.js

Top comments (0)