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
Verify your node and npm installations:
node -v
npm -v
3.cd into /var/www/html, create a folder where your node files will sit:
sudo mkdir node_project
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
Create a blank index.js file, which will be the entry point of the application.
sudo nano index.js
4.Initialize and install the various dependencies needed for a Node project to run, i.e. the node_modules folder:
npm init
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:
5.Edit the index.js file and bring in some quick-fire essential code to start up our node server:
6.Install dependencies needed in the node_modules folder. We only need express installed for this tutorial:
npm install express
Test your project and see if its running:
node index.js
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
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>
Save and close the file. Enable the config file:
sudo a2ensite node-project.conf
Enable proxy modules:
sudo a2enmod proxy proxy_http rewrite headers expires
Disable the default virtual host config file:
sudo a2dissite 000-default
Restart Apache:
sudo service apache2 restart
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
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
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
You should get the following interface which means that the service is running successfully:
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
``` 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
It will be something like this:
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
Changed enforcing to permissive.
Restart the server: ```js
sudo reboot
``` and then check the status:
sudo sestatus
``` 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 ```js
sudo nano /var/log/messages
``` 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$
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
```js
sudo sealert -l '*' | grep ‘<‘id_from messages’>
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
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
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
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
Where {USER} is your Linux server.
Navigate to your user's root directory and follow the following:
sudo nano react.sh
Copy the following code:
#!/bin/bash
. /home/{USER}/.nvm/nvm.sh
npm start
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)