This is the cheapest self-hosting solution for small projects. You can start a production-capable cloud compute instance for 5/mo, billed hourly.
Create an account on Linode
(Linode is now Akamai)
Referral link below. Free $100 credit, I get $25 if you end up spending $25:
https://www.linode.com/lp/refer/?r=43323ed57bf1cdb87800402242b6d3ea92843bfd
Create an Ubuntu linode and log in via ssh
Create a new Linode, select Ubuntu.
You can login using the root user, but best practice is creating a new user.
Install Node/NPM
Update our package list and upgrade our installed packages.
apt update
apt upgrade
apt install -y ca-certificates curl gnupg
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_21.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt update
sudo apt install nodejs -y
node -v
npm -v
If using Yarn
npm install --global yarn
Clone your project from Github
There are a few ways to get your files on to the server, I would suggest using Git. Create an SSH key for your server to authenticate with Github so you can pull repositories from your Github account.
git clone yourproject.git
Install dependencies and test app
cd yourproject
npm install
npm start
Stop app using ctrl+C
Setup PM2 process manager to keep your app running
In the root of your project directory, add a file called ecosystem.config.js and add the following
Add new file:
nano ~/yourproject/ecosystem.config.js
Paste this:
module.exports = {
apps: [
{
name: 'YourDomain.com',
exec_mode: 'cluster',
instances: 'max',
script: 'npm', // or yarn
args: 'start',
env: {
NODE_ENV: 'production',
PORT: 3000 // or your app's port number
},
}
]
}
Install PM2 using NPM
npm install pm2 -g
pm2 start
Or install PM2 using Yarn
yarn global add pm2
pm2 start
PM2 is used in place of NPM or Yarn. Other pm2 commands
pm2 show app
pm2 status
pm2 restart app
pm2 stop app
pm2 logs
pm2 flush
To make sure app starts when reboot
pm2 startup ubuntu
pm2 save
You should now be able to access your app using your IP and port.
Install NGINX and configure
Set up a server to proxy your app running on an IP address to run on a domain name.
apt install nginx
nano /etc/nginx/sites-available/yourdomain.com.conf
Add the following
server {
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000; #whatever port your app runs on
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;
}
}
Check NGINX config
nginx -t
Create a symlink from the config file in sites-enabled to the file you just created in sites-available.
ln -s /etc/nginx/sites-available/yourdomain.com.conf /etc/nginx/sites-enabled/
Restart NGINX
service nginx restart
You should now be able to visit your IP with no port (port 80) and see your app. Now let’s add a domain.
Add domain in Linode
On the Linode dashboard, go to Domains and add a domain.
Add an A record for “yourdomain.com” and for www to your droplet.
Register and/or setup domain from registrar.
Choose “Custom nameservers” and add these 3
ns1.linode.com
ns2.linode.com
ns3.linode.com
ns4.linode.com
ns5.linode.com
The domain might become available immediately, but it may take a bit to propagate.
Add SSL with LetsEncrypt
sudo apt-get update
sudo apt-get install python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Only valid for 90 days, test the renewal process with
certbot renew --dry-run
Now visit https://yourdomain.com and you should see your Node app.
Setup ufw firewall
Now we want to setup a firewall blocking access to the ports so all port traffic is obfuscated through domain names.
sudo ufw enable
sudo ufw status
sudo ufw allow ssh (Port 22)
sudo ufw allow http (Port 80)
sudo ufw allow https (Port 443)