We want to deploy and run our Node.js app on a generic "cloud" hosting provider Virtual Private Server (VPS) instance.
PM2 allows us to simplify continuous deployment from our chosen CI service and have more fine-grained control over how our App is run.
## What?
Use PM2 to deploy your Node.js App to a Digital Ocean Virtual Private Server (VPS) instance with built-in monitoring, great service quality and minimal cost.
In this guide we will be using the following:
- Digital Ocean Droplet (Virtual Private Server "VPS")
- CentOS (Operating System) - though any "mainstream linux" will work, and Ubuntu/Debian is the most popular.
- PM2 Process Manager.
- LetsEncrypt Free SSL Certificates.
If you do not already have a Digital Ocean account, please use the following link to register: https://m.do.co/c/29379863a4f8 and get $10 in Credit.
## Who?
Developers who have out-grown Heroku (pricing) and want to deploy to a different (affordable) hosting/cloud provider.
Note: Heroku has many useful features including Logging, Review Apps, permissions and teams which easily justify the cost. But if you have reached $100/month you should consider switching, provided you understand that you will need to work for it!
These are step-by-step instructions, follow them in order and don't skip steps!
Before we start, please ensure you have the following:
- Digital Ocean Account
(New to "DO"? Please use this link: https://m.do.co/c/29379863a4f8
to register)
- Public SSH Key uploaded to https://cloud.digitalocean.com/settings/security (so that you can login to the instance we are about to launch!) see: https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-digitalocean-droplets
- Basic Node.js knowledge
- 30 mins of time.
### 1. Create the DigitalOcean Instance
Go to: https://cloud.digitalocean.com/droplets/new and Create a Droplet!
Note: We are using a "blank" instance as opposed to a "One-click app", because this will show us how to setup "from scratch" and will thus be applicable to any cloud provider.
The instance we are creating is a CentOS 7.5 Droplet with 1GB RAM.
Select your desired region (datacenter); (pick the nearest to your users or dev team) e.g:
The default hostname
for the instance (based on the selected options)
is: centos-s-1vcpu-1gb-lon1-01
let's change that to: centos-nodejs-pm2
so that we know what the instance does from reading it's hostname.
Click Create and wait for the instance to be created.
Get the instance's IP (v4) Address, e.g: 206.189.26.154
from the UI:
and run the following command into your terminal to login to the instance via SSH:
ssh root@206.189.26.154
While logged in as root
run the update command:
yum update -y
There are security updates. Wait for the updates to run until you see
"Complete!":
#### 3.1 Install NVM
Install Node Version Manager (NVM) from GitHub:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
enable the nvm
command line tool by running the command:
source ~/.bashrc
Using NVM, install the latests (LTS) version of Node.js:
nvm install --lts
We install PM2 globally so that it can be used on the server.
npm install pm2 -g
Install Git CLI so that we can get the latest code from GitHub:
sudo yum install git -y
In order to use PM2 as our deployment tool we need to add the deployment key to:
- The "target" server.
In this case we will upload both the
public
key to the server so that TravisCI can access the server via SSH and theprivate
key so that the VPS can access GitHub togit pull
the latest version of the code. - GitHub as a "deploy key".
The
public
needs to be added as a "deploy key" for the repo so that GitHub will accept agit pull
request from the server.
If you don't already have a deployment key, see: encrypted-ssh-keys-deployment.md
Add the deployment SSH public
key
to the list of authorized_keys
on the VPS:
vi /root/.ssh/authorized_keys
Add the private
key to the server at ~/.ssh/deploy_key
:
then set the permissions on the key:
chmod 400 ~/.ssh/deploy_key
Follow the instructions in:
https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys
and add the deploy_key
to your project's "Deploy Keys" on GitHub.
e.g: https://github.com/nelsonic/hello-world-node-http-server/settings/keys
You should see:
Ensure that your App's project/repo has a ecosystem.config.js
file.
- Example:
ecosystem.config.js
- Config file Docs: https://pm2.io/doc/en/runtime/guide/ecosystem-file
- Deployment Docs: http://pm2.keymetrics.io/docs/usage/deployment/
Deploy the app using PM2 from your localhost:
pm2 deploy ecosystem.config.js production setup
pm2 deploy production exec "pm2 reload all"
PM2 will deploy the app on the default port (3000).
You can view the app by visiting: http://206.189.26.154:3000/
Checkpoint! This is our first sign of "success" but not a something we are going to send/show to end-users.
Install NGINX so that we can run it as a Proxy for our Node.js App. This will allow us to both HTTPS and multiple Apps on the same server listening on TCP port 80/443.
sudo yum install epel-release -y && sudo yum install nginx -y
Start NGINX with the command:
nginx
When you visit http://206.189.26.154 you should see:
This is our second "checkpoint".
In order to use NGINX as a proxy for our Node.js App, we need to setup a "proxy pass".
Edit the default nginx.conf
file:
vi /etc/nginx/nginx.conf
In the nginx.conf
file,
locate the section that starts with location / {
... e.g:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
Change:
location / {
}
To:
location / {
proxy_pass http://localhost:3000;
}
Stop and re-start nginx:
pkill nginx
nginx
Now when you visit http://206.189.26.154 you should see:
Now we are getting closer to something we can show an end-user; no TCP port in the URL.
Make an update to your app on your localhost
e.g: change "Hello World!" to "Hello PM2!"
Re-deploy the app from localhost
using the commands:
pm2 deploy ecosystem.config.js production update
pm2 deploy ecosystem.config.js production exec "pm2 reload all"
Now when you visit http://206.189.26.154 you should see:
Luckily deploying the App from Travis-CI is quite straightforward:
language: node_js
node_js:
- node
before_install: # setup the RSA key for use in Dokku Deployment:
- openssl aes-256-cbc -K $encrypted_77965d5bdd4d_key -iv $encrypted_77965d5bdd4d_iv
-in deploy_key.enc -out ./deploy_key -d
- eval "$(ssh-agent -s)"
- chmod 600 ./deploy_key
- echo -e "Host $SERVER_IP_ADDRESS\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- ssh-add ./deploy_key
after_success:
- npm install pm2 -g
- pm2 deploy ecosystem.config.js production update
- pm2 deploy ecosystem.config.js production exec "pm2 reload all"
Example:
.travis.yml
Sample output: https://travis-ci.org/nelsonic/hello-world-node-http-server/builds/446453771#L613
Login to your Domain Name Service and create a subdomain for your app:
Once you have added that, go refill your water glass/bottle while you wait for the DNS to propagate.
Create the pm2_dwyl_io.conf
file:
vi /etc/nginx/conf.d/pm2_dwyl_io.conf
And paste the following:
server {
server_name pm2.dwyl.io;
listen 80;
root /usr/share/nginx/html;
location / {
proxy_pass http://localhost:3000;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
Restart nginx:
nginx -t
# if the config test works run:
pkill nginx
nginx
Now when you visit your subdomain in your browser,
e.g: http://pm2.dwyl.io
you should see your app being served on the subdomain:
- Clustering Node.js apps in 3 minutes https://medium.com/@alaabatayneh/clustering-in-node-js-apps-a05e5a9ed444
- PM2 Process Manager - Zero Downtime — Performance Optimization - Part II https://medium.com/tech-tajawal/process-manager-pm2-performance-optimization-part-ii-6ca8e431a578
- Install NGINX: https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-centos-7
- NGINX upstream proxy: http://nginx.org/en/docs/http/ngx_http_upstream_module.html
- How to Configure NGINX (much better than the "official" docs!): https://www.linode.com/docs/web-servers/nginx/how-to-configure-nginx/
- Install Git on Centos (fairly obvs): https://www.digitalocean.com/community/tutorials/how-to-install-git-on-centos-7
- PM2 show process: https://futurestud.io/tutorials/pm2-list-processes-and-show-process-details