Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Node app deploy with nginx & SSL

Node.js Deployment

Steps to deploy a Node.js app to DigitalOcean using PM2, NGINX as a reverse proxy and an SSL from LetsEncrypt

1. Sign up for Digital Ocean

If you use the referal link below, you get $10 free (1 or 2 months) https://m.do.co/c/5424d440c63a

2. Create a droplet and log in via ssh

I will be using the root user, but would suggest creating a new user

3. Install Node/NPM

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -

sudo apt install nodejs

node --version

4. Clone your project from Github

There are a few ways to get your files on to the server, I would suggest using Git

git clone yourproject.git

5. Install dependencies and test app

cd yourproject
npm install
npm start (or whatever your start command)
# stop app
ctrl+C

6. Setup PM2 process manager to keep your app running

sudo npm i pm2 -g
pm2 start app (or whatever your file name)

# Other pm2 commands
pm2 show app
pm2 status
pm2 restart app
pm2 stop app
pm2 logs (Show log stream)
pm2 flush (Clear logs)

# To make sure app starts when reboot
pm2 startup ubuntu

You should now be able to access your app using your IP and port. Now we want to setup a firewall blocking that port and setup NGINX as a reverse proxy so we can access it directly using port 80 (http)

7. Setup ufw firewall

sudo ufw enable
sudo ufw status
sudo ufw allow ssh (Port 22)
sudo ufw allow http (Port 80)
sudo ufw allow https (Port 443)

8. Install NGINX and configure

sudo apt install nginx

sudo nano /etc/nginx/sites-available/default

Add the following to the location part of the server block

    server_name yourdomain.com www.yourdomain.com;

    location / {
        proxy_pass http://localhost:5000; #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
sudo nginx -t

# Restart NGINX
sudo 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

9. Add domain in Digital Ocean

In Digital Ocean, go to networking and add a domain

Add an A record for @ and for www to your droplet

Register and/or setup domain from registrar

I prefer Namecheap for domains. Please use this affiliate link if you are going to use them https://namecheap.pxf.io/c/1299552/386170/5618

Choose "Custom nameservers" and add these 3

  • ns1.digitalocean.com
  • ns2.digitalocean.com
  • ns3.digitalocean.com

It may take a bit to propogate

  1. Add SSL with LetsEncrypt
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-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

@walidabou

This comment has been minimized.

Copy link

@walidabou walidabou commented Oct 5, 2019

Yes! you are the best. Frankly I waited too long for this tutorial about LetsEncrypt.
I watched some tutorials about it but they didn't convince me much.

@gustavost26

This comment has been minimized.

Copy link

@gustavost26 gustavost26 commented Nov 4, 2019

Excellent tutorial.

@viniciusgusmao

This comment has been minimized.

Copy link

@viniciusgusmao viniciusgusmao commented Nov 4, 2019

Excellent tutorial, but I had some troubles during the proccess and I'd like to share it.

First, I had to add several DNS's to work the sudo update and other commands, like curl or npm packages

Google public name servers:
8.8.8.8
8.8.4.4
OpenDNS
208.67.222.222
208.67.220.220

This tutorial https://www.tecmint.com/set-permanent-dns-nameservers-in-ubuntu-debian/ helped me, because just change /etc/resolv.conf didn't work for me.

As I was using Ubuntu 18.04, I had to follow these instructions before using sudo add-apt-repository ppa:certbot/certbot.

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update

And then, install certbot using:

sudo apt-get install certbot python-certbot-nginx

The universe repos and software-properties must be installed before the PPA can be added.

After this, everything worked out.

The DigitalOcean support was fast and efficient in my doubts.

@debaosuidecl

This comment has been minimized.

Copy link

@debaosuidecl debaosuidecl commented Dec 5, 2019

Brad...
saving lives and careers since forever!!!

@81mark

This comment has been minimized.

Copy link

@81mark 81mark commented Dec 11, 2019

Thanks for this tutorial.

I had issues generating the SSH and logging into my droplet with Windows. I thought I would share what steps I took to get it working, in case anyone has issues with Windows.

This is what I had to do to get it working.

I had to use the Git Bash terminal.
Then used: ssh-keygen
enter a name: YOUR_CERTIFICATE_NAME
enter a passphrase if you wish:

Then generate the SSH after it will give you the directory that it was saved in

Now go to the directory and find the YOUR_CERTIFICATE_NAME.pub - open it and copy all text.
Now go to generate ssh on digital ocean ssh and paste it in.
Then below choose a name in the field below. example YOUR_CERTIFICATE_NAME

Back in GIT BASH...
Enter the following to Log in.

ssh root@DROPLET_IP -i ~\YOUR_CERTIFICATE_NAME

@damatanov99

This comment has been minimized.

Copy link

@damatanov99 damatanov99 commented Dec 17, 2019

This guy rocks

@Coyas

This comment has been minimized.

Copy link

@Coyas Coyas commented Dec 19, 2019

no need to coding ssl things inside node app?
Only add it on the webserver??

@transli

This comment has been minimized.

Copy link

@transli transli commented Dec 25, 2019

awesome saved me some time thanks man

@msrumon

This comment has been minimized.

Copy link

@msrumon msrumon commented Jan 3, 2020

Hell yeah! Exactly what I was looking for! But for CentOS, instead of Ubuntu. Never mind, these will also come in very handy! Many many thanks Brad!

@ehidalgo1988

This comment has been minimized.

Copy link

@ehidalgo1988 ehidalgo1988 commented Jan 15, 2020

hey I'm getting this error, when trying to renew the certification

nginx error invalid pid number in /run/nginx.pid

if someone could help me

@Slymasterfunk

This comment has been minimized.

Copy link

@Slymasterfunk Slymasterfunk commented Jan 23, 2020

I now have a new template to build more complex applications. Thanks!!!

@bobmacneal

This comment has been minimized.

Copy link

@bobmacneal bobmacneal commented Jan 28, 2020

Thanks Brad! That was the best video course I've ever taken. I followed the entire course step-by-step, but applied your ideas, patterns, and suggestions to the API I needed to back the React prototype I've been building. So cool to see my API docs serving up from Digital Ocean.

@Shabnam57

This comment has been minimized.

Copy link

@Shabnam57 Shabnam57 commented Feb 2, 2020

Thank you very much. The best tutorial I've ever watched ! After I edited default nginx file, I am getting this error. Any idea would be a great help!
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Sat 2020-02-01 19:59:11 EST; 7s ago
Docs: man:nginx(8)
Process: 3623 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=1/FAILURE)
Process: 3622 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)

Feb 01 19:59:10 server.cannaoui.ca nginx[3623]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Feb 01 19:59:10 server.cannaoui.ca nginx[3623]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
Feb 01 19:59:10 server.cannaoui.ca nginx[3623]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Feb 01 19:59:10 server.cannaoui.ca nginx[3623]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
Feb 01 19:59:11 server.cannaoui.ca nginx[3623]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Feb 01 19:59:11 server.cannaoui.ca nginx[3623]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
Feb 01 19:59:11 server.cannaoui.ca nginx[3623]: nginx: [emerg] still could not bind()
Feb 01 19:59:11 server.cannaoui.ca systemd[1]: nginx.service: Control process exited, code=exited status=1
Feb 01 19:59:11 server.cannaoui.ca systemd[1]: nginx.service: Failed with result 'exit-code'.
Feb 01 19:59:11 server.cannaoui.ca systemd[1]: Failed to start A high performance web server and a reverse proxy server.

@Shabnam57

This comment has been minimized.

Copy link

@Shabnam57 Shabnam57 commented Feb 2, 2020

I got it! Apache2 service was up!

@MyRibbonApps

This comment has been minimized.

Copy link

@MyRibbonApps MyRibbonApps commented Feb 13, 2020

How do i make updates to the code? Is it automatically made when i change the code and do a new commit?

@kquinn3

This comment has been minimized.

Copy link

@kquinn3 kquinn3 commented Feb 15, 2020

This is working great. Thanks Brad.

@umair-riaz-official

This comment has been minimized.

Copy link

@umair-riaz-official umair-riaz-official commented Feb 22, 2020

you are rocking man, God Bless you with more.

@BobDempsey

This comment has been minimized.

Copy link

@BobDempsey BobDempsey commented Mar 18, 2020

You're always saving me! Thanks again Brad. 😀

@Jassem

This comment has been minimized.

Copy link

@Jassem Jassem commented Mar 27, 2020

Nice tech stack, amazing presentation and effort.Thanks a lot Brad :)

@aminbaig

This comment has been minimized.

Copy link

@aminbaig aminbaig commented Mar 29, 2020

I deployed as per the the instructions. Everything was working fine and I could access my site using http. Once I used the certbot, now my site simple times out.

I have sopped and restarted nginx and pm2 but nothing. Please advise.

@nymhays

This comment has been minimized.

Copy link

@nymhays nymhays commented Mar 29, 2020

Don't forget to upgrade your packages after update with command "sudo apt upgrade" after "sudo apt update" , very cool tutorial Brad .

@Built-Better

This comment has been minimized.

Copy link

@Built-Better Built-Better commented Apr 18, 2020

Anyone finding that the app crashes when you refresh a sub route or directly access it in the browser? ie. https://appname.com/dashboard

Any solutions for Nginx to catch all and still send to the server?

@kquinn3

This comment has been minimized.

Copy link

@kquinn3 kquinn3 commented Apr 19, 2020

Anyone finding that the app crashes when you refresh a sub route or directly access it in the browser? ie. https://appname.com/dashboard

Any solutions for Nginx to catch all and still send to the server?

I had the same issue. I deployed an app I made where I used React in the frontend. In my case, when you click the backbutton or refresh, it could not catch my route, To fix this, I made a catchall route in my server.com that calls the compiled react code. This is what I added:

// Serve static assets in production

app.use(express.static(path.join(__dirname, "client/build")));
app.get("/*", (req, res) =>
  res.sendFile(path.join(__dirname, "client/build", "index.html"))
);

What did you do the frontend in? You probably need to add those routes to your server.js file.

@Built-Better

This comment has been minimized.

Copy link

@Built-Better Built-Better commented Apr 19, 2020

I have the same routes set up on my server for production settings, I also used a react front end.

// Serve static assets if in production
if (process.env.NODE_ENV === 'production') {
  // Set static folder
  app.use(express.static('client/build'));

  app.get('*', (req, res) => {
    res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
  });
}

My final solution in case anyone else ran into this problem was to alter my Nginx sites-available file to default to serving my built react app in index.html, and then proxy API routes afterward to my node app on the port which it runs.


server {
        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name buildingblockscms.tech www.buildingblockscms.tech;


        # react app & front-end files
        location / {
                root /home/dare/builtbetterCMS/client/build;
                try_files $uri /index.html;
        }

        location ~ ^/api(.*) {
                proxy_pass http://localhost:4000;
                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;
                proxy_set_header   X-Forwarded-Host $server_name;
                proxy_redirect     off;
        }
}
@arslaanmalik

This comment has been minimized.

Copy link

@arslaanmalik arslaanmalik commented Apr 25, 2020

How can we do the same in a docker container? I want to map 3 different node.js applications with 3 separate domains. Running on different ports e.g. 1 on 5000 , 2 on 5001 , 3 on 5002 now map it to example1.com example2.com example3.com what would be the process. Please help Thanks

@kquinn3

This comment has been minimized.

Copy link

@kquinn3 kquinn3 commented Apr 25, 2020

How can we do the same in a docker container? I want to map 3 different node.js applications with 3 separate domains. Running on different ports e.g. 1 on 5000 , 2 on 5001 , 3 on 5002 now map it to example1.com example2.com example3.com what would be the process. Please help Thanks

I started doing this a couple of months ago. I've also been wanting to create a gist so I created one with my method of doing what you want. Please look at it and let me know if there are any issues with my gist.
Adding Additional domains in nginx

@syncnsecure

This comment has been minimized.

Copy link

@syncnsecure syncnsecure commented Apr 25, 2020

Well i was thinking more of sharing the volume of the current certs with an nginx container networked with all of the containers and serverd at different ports maybe.

@cooper6101

This comment has been minimized.

Copy link

@cooper6101 cooper6101 commented May 2, 2020

Awesome post thank you. I have a question though that I can't seem to figure out. I am getting an error: no "ssl_certificate" is defined in server listening on ssl port while ssl handshaking. Has anyone experienced this and please how do I fix it?

@zotalabs-gaurav

This comment has been minimized.

Copy link

@zotalabs-gaurav zotalabs-gaurav commented May 12, 2020

If I want multiple sub domain name (subdomain1.mysite.com, subdomain2.mysite.com , subdomain3.mysite.com) to point the same node js app, what setting changes do I need to follow?

@mariobox

This comment has been minimized.

Copy link

@mariobox mariobox commented May 27, 2020

Thank you Brad! I've been looking for instructions for this exact process for months with no luck until I found your Youtube channel. Your work is amazing.

@devAnkitkr

This comment has been minimized.

Copy link

@devAnkitkr devAnkitkr commented Jun 16, 2020

Thanks Brad! I've learned a lot from this gist. But i have a problem. I am unable to call ajax/fetch operation in my react js website which i have deployed from netlify. I am getting this error:
This request has been blocked; the content must be served over HTTPS_
Is there a way to change digitalocean server to HTTPS intead of HTTP? or am i doing something wrong?!

@CristianCantilloDg

This comment has been minimized.

Copy link

@CristianCantilloDg CristianCantilloDg commented Jun 21, 2020

You're such a beast Brad. Like an 80% of all I learned is due to all your tutorials and guides. Thanks!

@kjx63

This comment has been minimized.

Copy link

@kjx63 kjx63 commented Jun 26, 2020

Ohhhh
This is what I was looking after for so long!
Thank you so much!

@kjx63

This comment has been minimized.

Copy link

@kjx63 kjx63 commented Jun 27, 2020

How do i make updates to the code? Is it automatically made when i change the code and do a new commit?

This is what I am also looking for the answer!

@virenv

This comment has been minimized.

Copy link

@virenv virenv commented Jun 27, 2020

Thank you very much for such a wonderful resource.

@KunalDholiya

This comment has been minimized.

Copy link

@KunalDholiya KunalDholiya commented Jul 2, 2020

How to use a subdomain for the same with aws ec2?

@achmadfatoni

This comment has been minimized.

Copy link

@achmadfatoni achmadfatoni commented Jul 3, 2020

after installing let's encrypt my application is not accessible, do i need to update nginx conf or else?

@achmadfatoni

This comment has been minimized.

Copy link

@achmadfatoni achmadfatoni commented Jul 3, 2020

after installing let's encrypt my application is not accessible, do i need to update nginx conf or else?

i use aws lightsail, fixed by allow https
image

@gtekelis

This comment has been minimized.

Copy link

@gtekelis gtekelis commented Jul 12, 2020

Fantastic!!

@ynonra

This comment has been minimized.

Copy link

@ynonra ynonra commented Jul 20, 2020

thanks a lot !!

@Rishabh570

This comment has been minimized.

Copy link

@Rishabh570 Rishabh570 commented Jul 24, 2020

This is great! But how do we provide the .env config to production. Is there any setting in digitalocean/aws ec2 regarding this?

@syncnsecure

This comment has been minimized.

Copy link

@syncnsecure syncnsecure commented Jul 29, 2020

I have a question what if we want to server multiple applications on different ports how can we server it with ssl. e.g. current app is running on port 5000, what if there is another app that runs on port 5001 how can we manage both to run on ssl.

@sujeetcarbazr

This comment has been minimized.

Copy link

@sujeetcarbazr sujeetcarbazr commented Aug 14, 2020

superb tutorial

@dumbperson98

This comment has been minimized.

Copy link

@dumbperson98 dumbperson98 commented Aug 21, 2020

How can I run multiple node projects and a subdomain on the same server? PLEASE HELP! Like i want test.example.com running a different app but on same server

@arslaanmalik

This comment has been minimized.

Copy link

@arslaanmalik arslaanmalik commented Aug 22, 2020

How can I run multiple node projects and a subdomain on the same server? PLEASE HELP! Like i want test.example.com running a different app but on same server

You can create new nginx configurations for different applications and then run it on the same server. ExampleA.com running app on port 5000 and ExampleB.com running on 5001 and vise versa. Just copy the deafult.conf file to deafult2.conf and edit it accordingly. Then create symbolic link. It should work

@iamphoenix310

This comment has been minimized.

Copy link

@iamphoenix310 iamphoenix310 commented Aug 23, 2020

What if I want to host multiple domains in one Droplet? Will I have to follow the same rules but just change the domain names? Please help me guys in this. Will I have to run all the steps? or just have to do nginx setting?

I did the same steps. but it failed now my website is stuck at 404 error. the certificate is installed but it's giving 404 error. I don't know how to set directories and server path to second domain.

@pkmaity

This comment has been minimized.

Copy link

@pkmaity pkmaity commented Aug 28, 2020

Thanks a lot for the tutorial 🙏🙏🙏

@vishnumanikantan

This comment has been minimized.

Copy link

@vishnumanikantan vishnumanikantan commented Aug 30, 2020

Thanks, Man!!!! it worked

@amoossssss

This comment has been minimized.

Copy link

@amoossssss amoossssss commented Sep 17, 2020

You just saved my day!
This is such a awesome, easy to read tutorial.
Appreciated.

@mayankgupta02

This comment has been minimized.

Copy link

@mayankgupta02 mayankgupta02 commented Sep 17, 2020

Hi Brad,
Thanks for the tutorial, it was really helpful! Had a quick question: how can we redeploy if we make changes? My hypothesis is:

  1. Stop pm2
  2. Delete the current git repository, clone the original one from github for the updated code
  3. Restart pm2

Is this correct? If not, could you please point out how it should be done?

Thanks!
Mayank

@Rishabh570

This comment has been minimized.

Copy link

@Rishabh570 Rishabh570 commented Sep 17, 2020

@iamphoenix310

This comment has been minimized.

Copy link

@iamphoenix310 iamphoenix310 commented Sep 17, 2020

Hi Brad,
Thanks for the tutorial, it was really helpful! Had a quick question: how can we redeploy if we make changes? My hypothesis is:

@mayankgupta02
You can simply do the following after cloning the git repository::

pm2 reload serverfile/start

That'll do the job without any downtime.

@HabeebCycle

This comment has been minimized.

Copy link

@HabeebCycle HabeebCycle commented Sep 20, 2020

Thanks, great steps.

@aytaa

This comment has been minimized.

Copy link

@aytaa aytaa commented Sep 27, 2020

how can i deploy one droplet mutli domain in nginx ?

@iamphoenix310

This comment has been minimized.

Copy link

@iamphoenix310 iamphoenix310 commented Sep 27, 2020

how can i deploy one droplet mutli domain in nginx ?

Check out this tutorial, it's in Hindi language: https://www.youtube.com/watch?v=ThsG92cmQfQ

@abelardogg

This comment has been minimized.

Copy link

@abelardogg abelardogg commented Sep 28, 2020

Since digitalocean now have ubuntu 20 I had some problems with the last step (SSL), so for the last part I just have followed these instructions (steps 4 and 5)

TL;DR
sudo snap install --classic certbot
sudo certbot --nginx

@dw2kim

This comment has been minimized.

Copy link

@dw2kim dw2kim commented Oct 12, 2020

Since digitalocean now have ubuntu 20 I had some problems with the last step (SSL), so for the last part I just have followed these instructions (steps 4 and 5)

TL;DR
sudo snap install --classic certbot
sudo certbot --nginx

omg.. you saved my 10 hours of struggling!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.