ingau's blog

This is a checklist for setting up a VPS for a nodejs application using pm2 and caddy.

[ ] SSH into the server. If you are using a cloud provider, you can find the public IP address in the console. You should either have the server’s private key or your local machine’s public key should be added to the server’s authorized keys. Here we assume the second case.

ssh <username>@<public_ip_address>
# Eg. ssh [email protected]

[ ] Setup firewall

sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable

If you are using a cloud provider, you may also need to configure the firewall on their end.

[ ] Update packages list

sudo apt-get update

[ ] Install git

sudo apt-get install git

[ ] Install node

# Node version 18.x
curl -sL | sudo -E bash
sudo apt-get install -y nodejs

[ ] Install PM2

sudo npm install pm2 -g

Ensure you have pm2 installed in your local machine as well.

[ ] Generate and copy SSH key

cat ~/.ssh/

Add the public key to your git provider.

[ ] Add Git provider to known hosts

[ ] Install and setup caddy

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf '' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf '' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt install caddy
sudo systemctl status caddy
sudo systemctl start caddy

[ ] Setup Caddyfile in local

<domain> {
  reverse_proxy localhost:3000

[ ] Setup ecosystem.config.cjs in local

module.exports = {
  apps: [
      name: '<instance_name>',
      script: './index.js',
      node_args: '--experimental-specifier-resolution=node',
      instances: 1,
      autorestart: true,
      exec_mode: 'cluster',
      watch: false,
      max_memory_restart: '4G',

  deploy: {
    production: {
      user: 'ubuntu',
      host: '',
      ref: 'origin/main',
      repo: '<git_remote>',
      path: '/home/ubuntu',
      'post-deploy': 'npm install && pm2 reload ecosystem.config.cjs && sudo cp ./Caddyfile /etc/caddy/Caddyfile && sudo systemctl reload caddy',

[ ] Setup PM2 ( in local )

pm2 deploy ecosystem.config.cjs production setup

[ ] Copy .env file to server with port set to 3000 ( or whatever port specified in Caddyfile )

pm2 deploy ecosystem.config.cjs production

[ ] Point domain A record to server public IP address.