VPS Setup with Nginx, UFW, PM2 and SSL/TLS

8/22/2025

PM2

PM2 (Process Manager 2) is a production-ready, open-source process manager for Node.js applications. It helps manage and monitor Node.js applications, ensuring they run continuously and can be restarted automatically if they crash. PM2 also offers features like log management, deployment, and load balancing.

OS: Ubuntu 22.04

Installation

Nodejs Install with NVM:

# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
# in lieu of restarting the shell
\. "$HOME/.nvm/nvm.sh"
# Download and install latest nodejs lts version
nvm install --lts
# Verify the Node.js version:
node -v # Should print "v22.14.0".
nvm current # Should print "v22.14.0".
# Verify npm version:
npm -v # Should print "10.9.2".

pm2 install:

npm install pm2@latest -g

Making app automatically run even after server restart:

pm2 startup systemd

Then run the output to terminal. Example output:

sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u yehezkiel1086 --hp /home/yehezkiel1086

Running Node Apps

Node native:

pm2 serve server.js 3500 --name "node-server"

React:

pm2 serve dist/ 3000 --name "react-client" --spa

Nextjs:

pm2 start npm --name volvinco -- start -- -p 5000

Nginx

NGINX is an open-source web server that can also be used as a reverse proxy, load balancer, caching, and more.

Install Nginx:

sudo apt-get update && apt-get install nginx -y

Simple Setting

/etc/nginx/sites-available/muncak-kuy.com

server {
	listen 80, 443;
	# could be ip addr or if already have domain name
	server_name www.muncak-kuy.com muncak-kuy.com;

	#root /var/www/html;

    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;
    }
    error_log /var/log/nginx/jarkom_error.log;
	access_log /var/log/nginx/jarkom_access.log;
}

Check for errors:

sudo nginx -t

Enable config:

sudo ln -s /etc/nginx/sites-available/muncak-kuy.com /etc/nginx/sites-enabled

Restart Nginx:

sudo service nginx restart

TLS/SSL

Create TLS cert

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt

Prompt output:

OutputCountry Name (2 letter code) [AU]:ID
State or Province Name (full name) [Some-State]:East Java
Locality Name (eg, city) []:Surabaya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Volvinco
Organizational Unit Name (eg, section) []:Department of IT
Common Name (e.g. server FQDN or YOUR name) []:203.175.11.22
Email Address []:volvinco@gmail.com

While using OpenSSL, you should also create a strong Diffie-Hellman (DH) group, which is used in negotiating Perfect Forward Secrecy with clients.

sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096

Configure Nginx to use SSL

Creating a Configuration Snippet Pointing to the SSL Key and Certificate

To properly distinguish the purpose of this file, name it self-signed.conf:

sudo nano /etc/nginx/snippets/self-signed.conf

Within this file, you need to set the ssl_certificate directive to your certificate file and the ssl_certificate_key to the associated key. This will look like the following:

/etc/nginx/snippets/self-signed.conf:

ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;

Creating a Configuration Snippet with Strong Encryption Settings

The parameters you set can be reused in future Nginx configurations, so you can give the file a generic name:

sudo nano /etc/nginx/snippets/ssl-params.conf

For your purposes, copy the provided settings in their entirety, but first, you will need to make a few small modifications.

First, add your preferred DNS resolver for upstream requests. We will use Google’s (8.8.8.8 and 8.8.4.4) for this guide.

Second, comment out the line that sets the strict transport security header. Before uncommenting this line, you should take a moment to read up on HTTP Strict Transport Security, or HSTS, and specifically about the “preload” functionality. Preloading HSTS provides increased security, but can also have far-reaching negative consequences if accidentally enabled or enabled incorrectly.

Add the following into your ssl-params.conf snippet file: /etc/nginx/snippets/ssl-params.conf

ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; 
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout  10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Disable strict transport security for now. You can uncomment the following
# line if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Adjusting the Nginx Configuration to Use SSL

Note: Use a 302 redirect until you have verified that everything is working properly. After, you will change this to a permanent 301 redirect.

/etc/nginx/sites-available/your_domain:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    #include snippets/self-signed.conf;
    #include snippets/ssl-params.conf;

	ssl_certificate /etc/ssl/certificate.crt; 
    ssl_certificate_key /etc/ssl/private.key;

root /var/www/your_domain/html;
        index index.html index.htm index.nginx-debian.html;
  
  server_name your_domain.com www.your_domain.com;

  location / {
                try_files $uri $uri/ =404;
        }
}

Next, add a second server block into the configuration file after the closing bracket (}) of the first block:

/etc/nginx/sites-available/your_domain.com:

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

    server_name your_domain.com www.your_domain.com;

    return 302 https://$server_name$request_uri;
}

Enabling Changes in Nginx

sudo nginx -t
sudo service nginx restart

Changing to Permanent Redirect

If your redirect worked correctly and you are sure you want to allow only encrypted traffic, you should modify the Nginx configuration to make the redirect permanent.

Find the return 302 and change it to return 301:

/etc/nginx/sites-available/your_domain.com

	return 301 https://$server_name$request_uri;

Check your configuration for syntax errors:

sudo nginx -t

When yourre ready, restart Nginx to make the redirect permanent:

sudo systemctl restart nginx