Skip to main content

Node.js

In the world of modern application development, Node.js has become one of the most popular technologies due to its ability to handle asynchronous processes and high performance. For those of us who use Apache Web Server on AlmaLinux 8, it is important to know how to integrate Node.js so that applications can run optimally, both for production and testing needs. This guide will discuss how to install Node.js on Apache Web Server on AlmaLinux 8 in a complete, detailed, and in-depth manner, including proxy configuration using mod_proxy, firewall settings, to deployment verification.

Prerequisites

  • Full root access
  • Apache/HTTPD installed
  • Basic Linux Command Line
  • Security
  • Domain (optional)

Install Node.js

Before installing Node.js, make sure your AlmaLinux 8 system is up to date and ready to accept new packages. Please update and add the epel repository:

info

EPEL (Extra Packages for Enterprise Linux) is essential to ensure all required additional dependencies are accessible.

dnf update -y
dnf install epel-release -y

To get the latest stable version of Node.js 22 and NPM, we will use the official repository from NodeSource:

curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
dnf install nodejs -y

Verify:

node -v
npmv -v

Example output:

v22.17.1

10.9.2

Apache Virtualhost for Node.js

Because the Apache Web Server cannot directly run Node.js applications, we need to configure mod_proxy to forward requests from port 80 to the Node.js application on port 3000, which Apache calls a Reverse Proxy for Node.js. If you don't have Apache installed, please install it using the following command:

dnf install httpd -y
systemctl enable --now httpd

Then install the required proxy module:

dnf install mod_proxy_html

Allow ports 80 and 443 on firewalld:

firewall-cmd --permanent --add-service={http,https}
firewall-cmd --reload

If you want to access your Node.js application directly on port 3000 (for development purposes), open that port as well:

firewall-cmd --add-port=3000/tcp --permanent
firewall-cmd --reload

Create a virtual host:

nano /etc/httpd/conf.d/focusnic.biz.id.conf

Fill in the following parameters:

/etc/httpd/conf.d/focusnic.biz.id.conf
<VirtualHost *:80>
ServerAdmin webmaster@focusnic.biz.id
ServerName focusnic.biz.id
ServerAlias www.focusnic.biz.id

ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/

ErrorLog /var/log/httpd/focusnic.biz.id-error.log
CustomLog /var/log/httpd/focusnic.biz.id-access.log combined
</VirtualHost>

Restart Apache to save changes:

apachectl configtest
systemctl restart httpd

Then, create a standard directory commonly used when using virtual hosts, although NodeJS applications can actually be run anywhere. This is especially important when managing more than one NodeJS project and for easier management:

mkdir -p /var/www/focusnic.biz.id/app1

Then create a hello.js file to display a simple program:

nano /var/www/focusnic.biz.id/app1/hello.js

Fill in the following script:

/var/www/focusnic.biz.id/app1/hello.js
const http = require('http');
const os = require('os');
const exec = require('child_process').exec;

const port = 3000;

const requestHandler = (req, res) => {
exec('npm -v', (err, npmVersion) => {
if (err) npmVersion = 'Unable to fetch NPM version';

const html = `
<html>
<head>
<title>Info Server Node.js</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
background-color: #f8f9fa;
}
.card {
background: #fff;
padding: 20px;
max-width: 600px;
margin: auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
border-radius: 8px;
}
h2 { color: #343a40; }
p { font-size: 1.1em; }
</style>
</head>
<body>
<div class="card">
<h2>Server Information</h2>
<p><strong>Server Time </strong> ${new Date().toLocaleString()}</p>
<p><strong>OS:</strong> ${os.type()} ${os.release()} (${os.platform()})</p>
<p><strong>Node.js ver:</strong> ${process.version}</p>
<p><strong>NPM ver:</strong> ${npmVersion.trim()}</p>
</div>
</body>
</html>
`;
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(html);
});
};

const server = http.createServer(requestHandler);
server.listen(port, () => {
console.log(`App running on http://localhost:${port}`);
});

Then run the app:

cd /var/www/focusnic.biz.id/app1/
node hello.js

Here is an example of output on the terminal:

info

To exit or stop the program, press CTRL+C and then check with the command ss -tulpn | grep 3000. If it still exists, kill the PID with the command kill -9 $PID.

App running on http://localhost:3000

The following is an example of the output when accessed via a browser http://$NAMA_DOMAIN

PM2

To ensure that the Node.js application continues to run even after the system is restarted, we can use PM2, the Node.js Process Manager:

Install PM2 globally:

npm install -g pm2
pm2 start hello.js --name helloapp

Here is an example of the output:

[PM2] Spawning PM2 daemon with pm2_home=/root/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /var/www/focusnic.biz.id/app1/hello.js in fork_mode (1 instance)
[PM2] Done.
┌────┬─────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼─────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ helloapp │ default │ N/A │ fork │ 173670 │ 0s │ 0 │ online │ 0% │ 42.1mb │ root │ disabled │
└────┴─────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

Save processes to run at boot:

pm2 startup systemd
pm2 save

Use --watch to autoreload when files change (development only):

pm2 start app.js --watch

Use --env to set the environment:

pm2 start app.js --env production

PM2: Basic Operations

NoCommandFunctionExample
1pm2 start <file> --name <nama>Run the Node.js app and name the processpm2 start app.js --name myapp
2pm2 listView all processes executed by PM2pm2 list
3pm2 show <nama>View details of a single processpm2 show myapp
4pm2 stop <nama>Stop the apppm2 stop myapp
5pm2 restart <nama>Restart the apppm2 restart myapp
6pm2 delete <nama>Removing an app from the PM2 process listpm2 delete myapp
7pm2 logs [nama]View app output logspm2 logs myapp

PM2: Advanced Operations

NoCommandFunctionExample
1pm2 startupMake PM2 run automatically at bootpm2 startup
2pm2 saveSaves the current process to be reloaded after a rebootpm2 save
3pm2 reload <nama>Reload app without downtime (hot reload)pm2 reload myapp
4pm2 start app.js -i maxRunning app in cluster mode (multi-core)pm2 start app.js -i max --name cluster-app
5pm2 monitReal-time monitoring of app processespm2 monit
6pm2 ecosystemCreating a configuration file template ecosystem.config.jspm2 ecosystem
7pm2 start ecosystem.config.jsRunning an app from a configuration filepm2 start ecosystem.config.js
8pm2 deploy ecosystem.config.js <env> [command]Automatic deployment via SSH (requires configuration ecosystem.config.js)pm2 deploy ecosystem.config.js production setup
9pm2 resurrectRestore previously saved processes (after reboot or crash)

NVM (Node Version Manager) (opsional)

Using NVM, we can manage multiple NodeJS versions. For example, versions 18, 20, 22, and so on. These are typically installed per user within a single environment or server, thus separating the production and development versions of NodeJS.

Execute the following command to install NVM:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
source ~/.bashrc

Then list the Node.js version with the following command:

nvm list-remote

Then install the latest version of Node.js 22:

nvm install 22

Then set the default Node.js version:

nvm use 22

Troubleshooting

  1. node: command not found

Check with the command which node or echo $PATH. If using NVM, make sure nvm.sh is executed in .bashrc

  1. Node.js version is not compatible

Use NodeSource for the latest version: curl -fsSL https://rpm.nodesource.com/setup_22.x

  1. npm: command not found

Use node -v to check the version. If the version is too old, uninstall and reinstall Node.js from NodeSource.

  1. nvm: command not found

Add to ~/.bashrc or ~/.bash_profile: export NVM_DIR="$HOME/.nvm and source ~/.nvm/nvm.sh

  1. Can't use Node.js with sudo

Avoid sudo when using NVM. If you need root access, install Node.js system-wide (via NodeSource)

  1. PM2 does not recognize the node on reboot

Use the Node.js version of NodeSource for production use or set $PATH manually in systemd

Conclusion

Node.js and NPM are the foundation for running server-side JavaScript applications. In AlmaLinux 8, the best installation for production is NodeSource, and NVM is only recommended in development environments due to its flexibility in switching Node.js versions. Use PM2 for Node.js process management.

Q: Do I need to install both Node.js and NVM?
A: No. Use one of these:

  • Node.js from NodeSource → for production
  • NVM → for developers who need multiple versions of Node.js

Q: How do I uninstall a Node.js version if it's already installed?
A:

  • If installed from dnf or NodeSource: dnf remove nodejs
  • If using NVM: nvm uninstall $VER_NODEJS

Q: How do I change the Node.js version with NVM?
A: Run the following command, for example, if you want to use `node v20

nvm install 20
nvm use 20

Q: Why isn't Node.js recognized after a reboot when using PM2?
A: Because NVM isn't global and isn't recognized by systemd. Solution:

  • Don't use NVM in production
  • Use Node.js from NodeSource
  • Or explicitly set the PATH in the systemd unit file

Q: Do I need to run Node.js applications as root?
A: Not recommended. Run as a regular user. Root is only used for installation and system configuration.