Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for How to Securely Deploy Node App to Ubuntu Server
coder7475
coder7475

Posted on • Edited on

     

How to Securely Deploy Node App to Ubuntu Server

This is blog about how you can deploy a Node app to a server with Nginx, whether it is aVPS,VDS, ordedicated server. This assumes you're familiar with basicLinux &git commands. This will work for any Node app that runs a server, be it anExpress app,Next.js app,Remix app, etc. Another thing to note is that we will deploy application code; the database will be separated.

Assumptions

  1. A hosting service with full root access and a domain
  2. OS: Ubuntu 22.04
  3. IPv4:172.172.172.172
  4. IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334
  5. Domain Name:example.com
  6. Using abash terminal on yourlocal computer
  7. Code is hosted onGitHub

Workflow Steps

1. Secure Your Server


A. Login to Your Server

  1. Open your terminal (bash, zsh, etc.)
ssh root@172.172.172.172
Enter fullscreen modeExit fullscreen mode

You will be prompted to provide the root password. After entering it, you will be logged into your server. You should see something like this:

root@vm172048:~$
Enter fullscreen modeExit fullscreen mode

If so, you have successfully logged in as the root user.

B. Server Hardening

Now that we are inside our machine, we can start installing the necessary packages and software, but before that, let's upgrade our system. Enter the command below in your terminal:

apt update&& apt upgrade-y
Enter fullscreen modeExit fullscreen mode

Now, all the current packages are updated to the latest patch version, which keeps the system safe from "unpatched vulnerability exploitation."

C. Create Non-Root User

Deploying as a root user is not recommended, as it has full access to all server resources. So let's create anon-root user calledadmin and add it to thesudo group to use commands that need root privileges.

To create asudo user:

useradd-m-s /bin/bash admin
Enter fullscreen modeExit fullscreen mode

This will create a new user with the nameadmin, and you can check the groups of the user using thegroups command.

groups admin
Enter fullscreen modeExit fullscreen mode

Let's add theadmin user to thesudo group:

usermod-aGsudoadmin
Enter fullscreen modeExit fullscreen mode

This will add the user to thesudo group without removing them from the originaladmin group.

Now let'screate a password for the user:

sudopasswd admin
Enter fullscreen modeExit fullscreen mode

You will be prompted to enter a new password and retype it.

Tocheck if the password is set properly, open a new terminal and check it by typing:

ssh admin@172.172.172.172
Enter fullscreen modeExit fullscreen mode

You will be prompted for your new password. After entering it, you should see something like:

admin@vm172048:~$
Enter fullscreen modeExit fullscreen mode

D. Connect to the Server Using SSH

Usingpassword login is not recommended. You want to use SSH (Secure Shell) and make sure thatSSH is the only way to log in.

If you are a user ofgit, chances are that you already have anssh key set up. If you don't already have anssh key, use the command below to generate a new SSH key on yourlocal machine:

ssh-keygen-t ed25519-C"your_email@example.com"-f ~/.ssh/example
Enter fullscreen modeExit fullscreen mode

Activate SSH agent:

eval"$(ssh-agent-s)"&& ssh-add ~/.ssh/example
Enter fullscreen modeExit fullscreen mode

Usecat ~/.ssh/example.pub to copy the key. Login to server and Then paste it into the server's~/.ssh/authorized_keys:

mkdir ~/.sshtouch ~/.ssh/authorized_keyssudonano ~/.ssh/authorized_keys
Enter fullscreen modeExit fullscreen mode

After all of this, you should be able to log in without using a password.

E. Disable Root and Password Login on the Server

To turn off username and password login, type in:

sudonano /etc/ssh/sshd_config
Enter fullscreen modeExit fullscreen mode

Find these values and set them as follows:

Port 1234# Change the default port (use a number between 1024 and 65535)PermitRootLogin no# Disable root loginPasswordAuthentication no# Disable password authenticationPubkeyAuthenticationyes# Enable public key authenticationAuthorizedKeysFile .ssh/authorized_keys# Specify authorized_keys file locationAllowUsers admin# Only allow specific users to log in
Enter fullscreen modeExit fullscreen mode

Thisdisallows every login method besides SSH under the user you copied your public key to. It stops login as root and only allows the user you specify to log in. HitCTRL+S to save andCTRL+X to exit the file editor. Restart SSH:

sudoservice ssh restart
Enter fullscreen modeExit fullscreen mode

Now try logging in as root to see if it disallows you. Since you changed the default SSH port from 22 to 1234, you need to mention the port when logging in.

ssh -p 1234 root@172.172.172.172
Enter fullscreen modeExit fullscreen mode

This will disallow you since root login is disabled. To log in, use:

ssh -p 1234 admin@172.172.172.172
Enter fullscreen modeExit fullscreen mode

Also, it should go without saying, butyou need to keep the private key safe, and if you lose it,you will not be able to get in remotely anymore.

F. Firewall Configuration

Ubuntu comes with theufw firewall by default. If not, you can install it with the command:

sudo apt install ufw -y
Enter fullscreen modeExit fullscreen mode

To see the current status ofufw, enter:

sudo ufw status
Enter fullscreen modeExit fullscreen mode

This will show the current status of the firewall. To enable the firewall, run the following command:

sudo ufw enable
Enter fullscreen modeExit fullscreen mode

First, run some default policies with:

sudo ufw default deny incoming && sudo ufw default allow outgoing
Enter fullscreen modeExit fullscreen mode

Now, since we changed the SSH port to1234, allow it through the firewall. Aside from that, we will be using ports 80 and 443 for web serving via HTTP and HTTPS, so allow them too.

sudo ufw allow 1234,80,443
Enter fullscreen modeExit fullscreen mode

To further improve brute force login via SSH, use the command below:

sudo ufw limit 1234
Enter fullscreen modeExit fullscreen mode

This will limit port 1234 to 6 connections in 30 seconds from a single IP. If you opened any port incorrectly, you can deny the connection with:

sudo ufw deny <port_number>
Enter fullscreen modeExit fullscreen mode

Now, to see the current status, use:

sudo ufw status verbose
Enter fullscreen modeExit fullscreen mode

Restart theufw to make sure all rules are applied:

sudo ufw reload
Enter fullscreen modeExit fullscreen mode

After enabling the firewall,neverexit from your remote server connection without enabling the rule for thessh connection. Otherwise,you won't be able to log into your own server.

G. Fail2Ban Configuration

Fail2Ban provides a protective shield for Ubuntu 22.04 that is specifically designed to block unauthorized access and brute-force attacks on essential services likeSSH and FTP. To installfail2ban, use the command below:

sudo apt install fail2ban
Enter fullscreen modeExit fullscreen mode

After the installation is complete, start the Fail2ban service with:

sudo systemctl start fail2ban
Enter fullscreen modeExit fullscreen mode

Toenable Fail2ban on Ubuntu 22.04 so that it starts automatically when your system boots up, use:

sudo systemctl enable fail2ban
Enter fullscreen modeExit fullscreen mode

Next, we need to verify if Fail2ban is up and running without any issues using the following command:

sudo systemctl status fail2ban
Enter fullscreen modeExit fullscreen mode

Now let's configure Fail2ban. The main configuration is located in/etc/fail2ban/jail.conf, but it's recommended to create a local configuration file:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Enter fullscreen modeExit fullscreen mode

Now open thejail.local file and tweak the values in the Default section:

bantime = 10mfindtime = 10mmaxretry = 5
Enter fullscreen modeExit fullscreen mode

Fail2Ban works by banning an IP for a specifiedban time after detecting repeated failures within a definedfind time. Themax retry setting determines the number of failures allowed before an IP is banned.

Now restart the Fail2ban service:

sudo systemctl restart fail2ban
Enter fullscreen modeExit fullscreen mode

To see which service for which jail is activated, enter:

sudo fail2ban-client status
Enter fullscreen modeExit fullscreen mode

If you have come this far, congratulations! You have secured your server. Now it's ready to deploy yourwebApp. Enterexit to end the session.

exit
Enter fullscreen modeExit fullscreen mode

2. DNS Configuration


Our website will be known by a domain name, in this case,example.com. To make the domain name point to our server, we need to do some DNS configuration on the side of our domain provider.

a. Login to your domain provider's website.

b. Navigate toexample.com and then manage DNS Management.

c. Now update theA andAAAA records for IPv4 & IPv6 Address.

Record TypeHost NameAddress
A@172.172.172.172
AAAA@2001:0db8:85a3:0000:0000:8a2e:0370:7334

d. Next, update theCNAME Record to forwardwww.example.com toexample.com.

Record TypeHost NameAddress
CNAMEwwwexample.com

CNAME maps a subdomain to another domain name.

Now it might take a few minutes to propagate to all DNS servers. To check ifexample.com resolves to your hostIP address, check DNS propagation using this online tool:DNS Checker.

3. Deploy the Web App


To deploy, we will need to install several packages. First, we will be usinggit to clone the repository fromGitHub. Since it's a Node app, we will needNode installed on our system; I will be using Node version 20. We will be usingnpm, which comes withNode. Finally, to manage the app as a background process, we will be usingpm2.

First, log in to your server using:

ssh -p 1234 admin@172.172.172.172
Enter fullscreen modeExit fullscreen mode

A. Install All Necessary Dependencies

a. First, check the installation:

nginx -vnode -vnpm -vgit --versionpm2 --version
Enter fullscreen modeExit fullscreen mode

b. Then update to the latest version & remove unnecessary packages:

sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y
Enter fullscreen modeExit fullscreen mode

c. Install the latest version ofgit available:

sudo apt install git -y
Enter fullscreen modeExit fullscreen mode

d. InstallNode version lts & its accompanyingnpm (Node Package Manager) using nvm:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bashsource ~/.bashrcnvm install --ltsnode -v
Enter fullscreen modeExit fullscreen mode

e. Install thenginx web server:

sudo apt install -y nginx
Enter fullscreen modeExit fullscreen mode

f. Install pm2:

sudonpm-ginstallpm2
Enter fullscreen modeExit fullscreen mode

B. Clone the Code Repo from GitHub

Our code repo will be hosted onGitHub. We will be cloning the repo to our server using a deploy key.

a. First, generate a deploy key namedexample:

ssh-keygen -t ed25519 -C "your_email@gmail.com" -f ~/.ssh/example
Enter fullscreen modeExit fullscreen mode

You will be prompted for a passphrase; just press enter. This will generate a public-private key pair calledexample.pub &example in your~/.ssh folder.

b. Make sure the~/.ssh folder is owned byadmin:

sudo chown -R admin ~/.ssh
Enter fullscreen modeExit fullscreen mode

c. Add GitHub's SSH server public key to the server'sknown_hosts file:

ssh-keyscan -t ed25519 github.com >> ~/.ssh/known_hosts
Enter fullscreen modeExit fullscreen mode

d. Next, copy the SSH Public key after outputting the key to the terminal:

cat ~/.ssh/example.pub
Enter fullscreen modeExit fullscreen mode

e. Use the copied key as a deploy key in GitHub:

  • Go to your GitHub Repo
  • Click on the Settings Tab
  • Click on Deploy Keys option from the sidebar
  • Click on the Add Deploy Key Button and paste the copied SSH Public Key with a name of your choice
  • Click on Add Key

f. Clone the project from your GitHub Repo to your server's home using:

git clone git@github.com:admin7374/example_app.git
Enter fullscreen modeExit fullscreen mode

Here,admin7374 is the GitHub username andexample_app is the Node app we are about to deploy. This will clone the code repo on the server.

C. Run the App with pm2

Now it's time to build and run the Node app in the background:

a. Navigate to the project folder:

cd ~/example_app
Enter fullscreen modeExit fullscreen mode

b. Create a.env file:

touch .env
Enter fullscreen modeExit fullscreen mode

c. Open the.env file and paste your environmental variables:

sudo nano .env
Enter fullscreen modeExit fullscreen mode

Example.env file:

PORT = 8001DATABASE_URL = "database_url"
Enter fullscreen modeExit fullscreen mode

After pasting, clickCTRL + S andCTRL + X to save and exit.

d. Create anecosystem.config.cjs file in your repo code (best created inside the GitHub repo):

touch ecosystem.config.cjssudo nano ecosystem.config.cjs
Enter fullscreen modeExit fullscreen mode

Then paste the code below:

module.exports = {  apps : [      {        name: "example_app",        script: "npm start",        port: 8001 // optional, if have port set in app      }  ]}
Enter fullscreen modeExit fullscreen mode

The above code will run the Node app at port8001; make sure it matches the port defined in the application. Thescript is usually how the Node app generally runs. It assumes there is annpm start script inside yourpackage.json to run the build code.

e. Next, install the necessary Node modules using:

npm ci
Enter fullscreen modeExit fullscreen mode

The above command creates anode_modules folder with all necessary packages to run the code.

f. Now, let's build the code. Type:

npm run build
Enter fullscreen modeExit fullscreen mode

The above script will build the code for distribution using thebuild script defined inpackage.json.

g. Add PM2 Process on Startup:

sudo pm2 startup
Enter fullscreen modeExit fullscreen mode

h. Start the Node App usingpm2:

pm2 start ecosystem.config.cjs
Enter fullscreen modeExit fullscreen mode

i. Save the PM2 Process:

pm2 save
Enter fullscreen modeExit fullscreen mode

This will save the process to keep running in the background.

j. List all PM2 processes running in the background:

pm2 list
Enter fullscreen modeExit fullscreen mode

k. If you need to reload for redeployment, use:

pm2 reload example_app
Enter fullscreen modeExit fullscreen mode

l. To check the PM2 process logs, use:

pm2 monit
Enter fullscreen modeExit fullscreen mode

This will open an interactive terminal that will show you logs and metadata for each process. Enterq to quit.

m. Check if the app is working properly using:

curl localhost:8001
Enter fullscreen modeExit fullscreen mode

This should output properly if the app is working.

D. Serve the App with NGINX

Now it's time to finally serve the app using Nginx. Nginx is a powerful web server, reverse proxy, and load balancer. In this case, we will use thenginx reverse proxy feature to serve the app running onlocalhost:8001 to the internet.

a. Start and enablenginx:

sudo systemctl start nginxsudo systemctl enable nginx
Enter fullscreen modeExit fullscreen mode

b. Verifynginx is up and running:

sudo systemctl status nginx
Enter fullscreen modeExit fullscreen mode

If everything went well, the output should indicate that the Nginx service isactive (running).

c. If you wish to confirm Nginx's operation via a web browser, navigate to:

http://example.com
Enter fullscreen modeExit fullscreen mode

This should show the defaultnginx page.

d. If it's not showing, theufw firewall may be blocking ports80 and443. To allow them through theufw firewall, use:

sudo ufw allow 'Nginx Full'
Enter fullscreen modeExit fullscreen mode

e. Nginx, like many server software, relies on configuration files to dictate its behavior. Begin by creating a configuration file for your website:

sudo nano /etc/nginx/sites-available/example.com
Enter fullscreen modeExit fullscreen mode

f. Inside this file, input the following proxy pass configuration:

server {    listen 80;    listen [::]:80;    server_name example.com www.example.com;    location / {        proxy_pass http://localhost:8001;        # Proxy Params - pass client request information to the proxied server        proxy_set_header Host $host;        proxy_set_header X-Real-IP $remote_addr;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_set_header X-Forwarded-Proto $scheme;        # If you need to upload files larger than 1M, use the below directive        # client_max_body_size 500M;        # Web socket upgrade configuration        # Uncomment the following lines if you're using websockets in your app        # proxy_http_version 1.1;        # proxy_set_header Upgrade $http_upgrade;        # proxy_set_header Connection "upgrade";    }    # Logging    access_log /var/log/nginx/example.com.access.log;    error_log /var/log/nginx/example.com.error.log warn;}
Enter fullscreen modeExit fullscreen mode

The proxy pass configuration serves files directly; it proxies requests to a local application (in this case, running on port 8001).

g. With the configuration file created, it isn't live yet. To activate it, you'll create a symbolic link to thesites-enabled directory:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
Enter fullscreen modeExit fullscreen mode

Think of this step as "publishing" your configuration, making it live and ready to handle traffic.

h. Test the configuration before going live:

sudo nginx -t
Enter fullscreen modeExit fullscreen mode

Nginx will then parse your configurations and return feedback. A successful message indicates that your configurations are error-free.

i. Time to go live. This requires a reload:

sudo systemctl reload nginx
Enter fullscreen modeExit fullscreen mode

j. Now check in the web browser, go to:

http://example.com
Enter fullscreen modeExit fullscreen mode

k. Our current website configuration serves content over HTTP on port 80, which is unencrypted. Let's encrypt it viaLet's Encrypt. First, install certbot:

sudo apt updatesudo apt install software-properties-commonsudo add-apt-repository ppa:certbot/certbotsudo apt updatesudo apt install certbot python3-certbot-nginx
Enter fullscreen modeExit fullscreen mode

l. Generate SSL Certificates using certbot:

sudo certbot --nginx -d example.com -d www.example.com
Enter fullscreen modeExit fullscreen mode

Just follow the instructions in the prompts. Your website will be encrypted with a proper SSL/TLS encryption certificate. Check your website in the browser to see if it's installed.

m. Nowadays, certbot, when getting a new cert, will set up auto-renew for you, so it's a sit-and-forget kind of task. But to make sure it worked, you can run:

sudo systemctl status certbot.timer
Enter fullscreen modeExit fullscreen mode

Now, big congratulations! You have successfully deployed your web app using Nginx. If you want to optimize Nginx, I recommend following this post:Basic Nginx Setup.

This concludes my documentation onhow to deploy a Node app securely with Nginx.

References - For More Information

  1. Server Setup & Hardening
  2. Server Hardening Best Practices
  3. Server Setup Basics
  4. Install fail2ban
  5. Fail2ban Ubuntu Configs
  6. Deploy Node.js to VPS
  7. Nginx Configs
  8. How to Change Default SSH Port
  9. Uncomplicated Firewall
  10. DNS Cheatsheet
  11. SCP
  12. PM2 Guide

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I am a Software Engineer focusing on web development. I am currently exploring the world of DevOps Engineering.
  • Joined

More fromcoder7475

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp