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

@mckiersey
Copy link

mckiersey commented Jul 28, 2021

Although it's already been said many times, I nonetheless think it's worth repeating: This is an excellent guide, thank you.
However, I wanted to add a note for those like me who get tripped up on step 8.

I am on an AWS EC2 ubuntu machine and for some reason those machines don't seem to have the sites-available config files. Rather there is a nginx.config file that feeds off a default.config file, which in turn is in a config.d directory (it's a weird directory name).

Anyway, it seems like sites-available and default.config (or whatever_name_you_want.config) both do the same thing, i.e. offer configuration to ngnix.config, so you just need to edit the default config file.

Here's what mine looks like (sorry for the screenshot, I wasn't able to figure out how to copy & paste from vim):
Screenshot 2021-07-28 at 22 56 26

@emarceenyusef
Copy link

emarceenyusef commented Aug 19, 2021

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;
        }
}

thanks a lot for This. I found no other tutorial that explains this situation. This is actually the important stuff cuz one will properly have frontend and backend on the same server.

@muhammadsiddiqbe
Copy link

muhammadsiddiqbe commented Aug 28, 2021

Now apt install python-certbot-nginx not working use apt install python3-certbot-nginx instead

@Mancunia
Copy link

Mancunia commented Sep 1, 2021

Great stuff

@edrick27
Copy link

edrick27 commented Sep 9, 2021

thanks a lot

@mrzacq
Copy link

mrzacq commented Sep 12, 2021

work like a charm, for the first time i thought my domain configuration is the problem because i can't go to my domain but after add certbot for ssl it's work like a charm.

thanks a lot brad

@ahmedRSA
Copy link

ahmedRSA commented Sep 24, 2021

if i want to host it on subdomain i have to config like this?

server_name sub.example.com www.sub.example.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;
    }

@jasbirrajrana
Copy link

jasbirrajrana commented Nov 12, 2021

Thanks man 😍😍

@fngadiyo
Copy link

fngadiyo commented Nov 21, 2021

if i want to host it on subdomain i have to config like this?

server_name sub.example.com www.sub.example.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;
    }

hi, I just deployed my subdomain and I think it is better to not use www.sub.examle.com, only use sub.example.com. Hope you already resolved it by yourself!

@Subhankhalid1
Copy link

Subhankhalid1 commented Jan 10, 2022

Its superb and help me out to deploy the nodejs app on digital oceans.

@kamran-12
Copy link

kamran-12 commented Jan 26, 2022

I am getting:

dpkg: error processing archive /var/cache/apt/archives/nodejs_12.22.9-deb-1nodesource1_amd64.deb (--unpack):
 trying to overwrite '/usr/share/doc/nodejs/api/fs.html', which is also in package nodejs-doc 12.22.5~dfsg-2~11u1
dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)
Errors were encountered while processing:
 /var/cache/apt/archives/nodejs_12.22.9-deb-1nodesource1_amd64.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

How to fix this?

@yuis-ice
Copy link

yuis-ice commented Feb 2, 2022

Helpful. Thanks:)

@kaanuki
Copy link

kaanuki commented Apr 11, 2022

I can't seem to run POST requests with this configuration. When I post i get this message, all the post data is undefined

Help!

Thanks in advance

@Dawsoncodes
Copy link

Dawsoncodes commented May 7, 2022

Thank you brad.

@ArianNexux
Copy link

ArianNexux commented May 18, 2022

Thank you, So cool!

@ranjandsingh
Copy link

ranjandsingh commented May 18, 2022

Thanks brad

@ranjandsingh
Copy link

ranjandsingh commented May 18, 2022

I can't seem to run POST requests with this configuration. When I post i get this message, all the post data is undefined

Help!

Thanks in advance

Some Times It does due to using HTTP instead of HTTPS please use HTTPS if you installed SSL already hope it helps.

@muhammadqazi
Copy link

muhammadqazi commented May 21, 2022

Best one !

@MDMohsinGhazi
Copy link

MDMohsinGhazi commented Jun 1, 2022

Getting this after configuring all configuration on EC2-instance
"ErrorResponse is not defined"

@lisandrofernando
Copy link

lisandrofernando commented Jun 8, 2022

Great tutorial, but my node website is not loading the css and javascript files, any idea how to fix it?

@MuttakinHasib
Copy link

MuttakinHasib commented Jun 11, 2022

how can I redirect from v2.example.com to example.com?
@bradtraversy

@0xhiro
Copy link

0xhiro commented Jun 12, 2022

This was so helpful. Thanks a lot!

@GoodnessEzeokafor
Copy link

GoodnessEzeokafor commented Jun 12, 2022

awesome

@Mihaidev-cloud
Copy link

Mihaidev-cloud commented Jun 29, 2022

For people who don't want to bother their minds with this, I created an automatic installer! https://github.com/Mihaidev-cloud/Nodejs-Webserver-AutoInstaller-Nginx

@0xhiro
Copy link

0xhiro commented Jun 29, 2022

I think I’d rather bother my mind with knowing how to configure the most basic server. Thanks a lot though @Mihaidev-cloud

@bishoplee
Copy link

bishoplee commented Jul 23, 2022

This is a life-saver. Great work Brad. Thanks also to all the inline comments.

@serfoll
Copy link

serfoll commented Sep 2, 2022

This was great, works well with google cloud too

@vinsonw
Copy link

vinsonw commented Oct 17, 2022

Successfully deployed on vultr. Left out the security and ssl part due to time limit.
Great course, learned a lot, thanks!

@hall500
Copy link

hall500 commented Oct 30, 2022

www.example.com, www is a subdomain already. so you can only use sub.example.com.

@mellifluus
Copy link

mellifluus commented Nov 5, 2022

Thanks! This was extremely helpful. For anyone having trouble with POST requests, the problem is NGINX redirects requests to https with status code 301, which sometimes changes the method of the request to GET. The solution is to redirect requests with status code 308, which doesn't change the method of the request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment