How to Deploy a Next.js App on a VPS Using GitHub Actions CI/CD Pipeline

ASHISH Gupta

ASHISH Gupta

17 min read

  • nextjs
  • cicd
  • vps
  • hosting
  • github
how to deploy next js app in vps using ci-cd pipline

Introduction

Are you finding the way to host your production website in markets to interact with users. Web hosting is an online service that makes your website’s content accessible on the internet. When you purchase a hosting plan, you are renting space on a physical server to store all the website’s files and data. after writing a production level code you want to Host your nextjs project without any trouble just like big mncs,

in this blog we'll see how to host nextjs project on VPS (Virtual Private Server) and creating CI-CD pipe line with Github Actions.

what is CI-CD Pipeline

A continuous integration and continuous deployment (CI/CD) pipeline is a series of steps that must be performed in order to deliver a new version of software. CI/CD pipelines are a practice focused on improving software delivery throughout the software development life cycle via automation. 

By automating CI/CD throughout development, testing, production, and monitoring phases of the software development lifecycle, organizations are able to develop higher quality code, faster and more securely. Although it’s possible to manually execute each of the steps of a CI/CD pipeline, the true value of CI/CD pipelines is realized through automation.

What is VPS

A virtual private server(VPS) is a machine that hosts all the software and data required to run an application or website. It is called virtual because it only consumes a portion of the server's underlying physical resources which are managed by a third-party provider. However, you get access to your dedicated resources on that hardware.

without wasting time let's start.

There Are following steps we'll discuss all steps in following sequence.

step 1: Book a domain and vPS

Register a domain name for your website because we are hosting for production level not for only college level project so based on your project book domain name like example.com or .in based on your need

doamin register

I have already a domain name so that i did not buy again this. but you can book something else.

Now purchase a VPS from any other website where you like. but remember a main thing you have to choose vps details based on your specific need like if you need heavy computing go on to the higher processor and for good computing power and if you want to stream videos so you have to remember for higher Bandwidth.

vpspurchaseing

For me i select most popular plan kvm2 for my project.

Step 2: ADD .env to your project

Go to your Github repositories of your project. on top Nav select Settings> on sidebar select Secrets and Variables>Actions.

Step 1:

secretes and variable set

Now click new repository secrets for adding your env key and value:

secretsaddnewrepos

Now add your envs value and pass key here,repeat these steps while your all env key pair are not over. given urls are example or dummy url don't copy it use your own Database Url.

envkeyvaluepairs

Step 3: Connect with VPS

open your vps provider website and find manage vps go to the ssh access and copy the command "ssh user@ipv4", example "ssh rootuser@192.143.0.9" copy this command and paste in your terminal

What is SSH access?

SSH (Secure Shell or Secure Socket Shell) is a network protocol that gives users -- particularly systems administrators -- a secure way to access a computer over an unsecured network. SSH also refers to the suite of utilities that implement the SSH protocol.

ssh rootuser@192.143.0.9 //example
sshaccess

After giving this command in your terminal you got simply enter yes for future access in your local personal computer.

C:\Users\tab>ssh root@194.434.19.45
The authenticity of host '194.434.19.45 (194.434.19.45)' can't be established.
ED25519 key fingerprint is SHA256:xK1XNPDd+mmdfDqtIEz6+JbgVrYaM3mX/xdfS5vS875ZgY. //this is fingerprint key which used logged in easily for future.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '194.434.19.45' (ED25519) to the list of known hosts.
root@194.434.19.45's password:// 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-119-generic x86_64)

here i already created my password if you don't you have to set password in your vps server with your provider.

ssh password creation

Here is your sections to change your password in your vps provider. After login your vps interface will look like this.

Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-119-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Sat Aug 31 05:42:56 UTC 2024

  System load:  0.02               Processes:             136
  Usage of /:   25.5% of 96.73GB   Users logged in:       0
  Memory usage: 38%                IPv4 address for eth0: 194.434.19.45
  Swap usage:   0%                 IPv6 address for eth0: 2a02:3780:12:f546::1

 * Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
   just raised the bar for easy, resilient and secure K8s cluster deployment.

   https://ubuntu.com/engage/secure-kubernetes-at-the-edge

Expanded Security Maintenance for Applications is not enabled.

14 updates can be applied immediately.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


Last login: Fri Aug 30 06:56:49 2024 from 103.71.255.176
root@sarvmandir:~#

This is your root user enter command this command

root@sarvmandir:~# sudo apt update
root@sarvmandir:~# sudo apt upgrade

you are ready to host your project,we don't host directly project in your root user because in future you may host multiple projects or multiple different ideas in your user so we make a different user and give root permissions, follow these steps.

step 4: Create the new user

  1. Create the new user: Open the terminal and use the adduser command to create a new user. Replace username with the desired username for example biyondbytes.

sudo adduser username

example:
root@sarvmandir:~# sudo adduser studio
Adding user `studio' ...
Adding new group `studio' (1004) ...
Adding new user `studio' (1004) with group `studio' ...
Creating home directory `/home/studio' ...
Copying files from `/etc/skel' ...
New password://enter a new password
Retype new password:
passwd: password updated successfully
Changing the user information for studio
Enter the new value, or press ENTER for the default
        Full Name []: biyondbytes
        Room Number []:for nothing press enter
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n] y

*refer this and do same thing for your project but choose username and password according to your choice.

  1. Add the user to the sudo group: Adding the user to the sudo group grants them root permissions. Execute the following command:

sudo usermod -aG sudo username

The -aG option in the sudo usermod -aG username command stands for:

  • -a (append): This option appends the user to the supplementary groups mentioned without removing them from any other groups they might already be a part of. If you don't use -a, the user will be removed from all other groups except the one you specify.

  • -G (group): This option specifies the group(s) to which you want to add the user. You can list multiple groups separated by commas.

  1. Verify the user's sudo privileges: You can verify that the user has sudo privileges by switching to the new user and running a command with sudo. For example:

su - username
sudo whoami
  1. Login to your user with this command where we setup your project in your terminal.

sudo su - username

for reference here is your example.

root@sarvmandir:~# sudo usermod -aG sudo studio
root@sarvmandir:~# sudo su - studio
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
studio@sarvmandir:~$

step 5:Installing Node Using the Node Version Manager

Another way of installing Node.js that is particularly flexible is to use nvm, the Node Version Manager. This piece of software allows you to install and maintain many different independent versions of Node.js, and their associated Node packages, at the same time.

To install NVM on your Ubuntu 20.04 machine, visit the project’s GitHub page. Copy the curl command from the README file that displays on the main page. This will get you the most recent version of the installation script.

Before piping the command through to bash, it is always a good idea to audit the script to make sure it isn’t doing anything you don’t agree with. You can do that by removing the | bash segment at the end of the curl command:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh

Review the script and make sure you are comfortable with the changes it is making. When you are satisfied, run the command again with | bash appended at the end. The URL you use will change depending on the latest version of nvm, but as of right now, the script can be downloaded and executed with the following:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash

This will install the nvm script to your user account. To use it, you must first source your .bashrc file:

source ~/.bashrc

Now, you can ask NVM which versions of Node are available:

nvm list-remote

Output. . .

        v20.7.0
        v20.8.0
        v20.8.1
        v20.9.0   (LTS: Iron)
       v20.10.0   (LTS: Iron)
       v20.11.0   (LTS: Iron)
       v20.11.1   (LTS: Iron)
       v20.12.0   (LTS: Iron)
       v20.12.1   (LTS: Iron)
       v20.12.2   (LTS: Iron)
       v20.13.0   (LTS: Iron)
       v20.13.1   (LTS: Iron)
       v20.14.0   (LTS: Iron)
       v20.15.0   (LTS: Iron)
       v20.15.1   (LTS: Iron)
       v20.16.0   (LTS: Iron)
->     v20.17.0   (Latest LTS: Iron)
        v21.0.0
        v21.1.0
        v22.6.0
        v22.7.0

It’s a very long list. You can install a version of Node by writing in any of the release versions listed. For instance, to get version v14.10.0, you can run:

nvm install v20.17.0

You can view the different versions you have installed by listing them:

nvm list

Output->     ->     v20.17.0
default -> v20.17.0
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v20.17.0) (default)
stable -> 20.17 (-> v20.17.0) (default)
lts/* -> lts/iron (-> v20.17.0)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/iron -> v20.17.0
. . .

This shows the currently active version on the first line (-> v14.10.0), followed by some named aliases and the versions that those aliases point to.

Note: if you also have a version of Node.js installed through apt, you may receive a system entry here. You can always activate the system-installed version of Node using nvm use system.

Additionally, there are aliases for the various long-term support (or LTS) releases of Node:

lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12 (-> N/A)
lts/fermium -> v14.21.3 (-> N/A)
lts/gallium -> v16.20.2 (-> N/A)
lts/hydrogen -> v18.20.4 (-> N/A)
lts/iron -> v20.17.0

You can install a release based on these aliases as well. For instance, to install the latest long-term support version, hydrogen, run the following:

nvm install lts/hydrogen

OutputDownloading and installing node v18.13.0...
. . .
Now using node v18.13.0 (npm v8.19.3)

You can switch between installed versions with nvm use:

nvm use v20.17.0

OutputNow using node v14.10.0 (npm v6.14.8)
```

You can verify that the install was successful using the same technique from the other sections:

```command
node -v

Outputv20.17.0

The correct version of Node is installed on your machine as expected. A compatible version of npm is also available.

Removing Node.js

You can uninstall Node.js using apt or nvm, depending on how it was installed. To remove the version from the system repositories, use apt remove:

sudo apt remove nodejs

By default, apt remove retains any local configuration files that were created since installation. If you don’t want to save the configuration files for later use, use apt purge:

sudo apt purge nodejs

To uninstall a version of Node.js that you installed using nvm, first determine whether it is the current active version:

nvm current

If the version you are targeting is not the current active version, you can run:

nvm uninstall node_version

OutputUninstalled node node_version

This command will uninstall the selected version of Node.js.

If the version you would like to remove is the current active version, you first need to deactivate nvm to enable your changes:

nvm deactivate


Step 6 : Setup Actions Runner

Que- what is GitHub actions runner?

Ans- Runners are the machines that execute jobs in a GitHub Actions workflow.

go to your GitHub repository and then setting>sidebar>actions>runner>New Self-hosted Runner.

After clicking on self hosted runner you some commands for your Linux vps copy and paste all the commands in your terminal.

setupactionsrunner

After following these commands you may get this interface.


# Authentication


√ Connected to GitHub

# Runner Registration

Enter the name of the runner group to add this runner to: [press Enter for Default]

Enter the name of runner: [press Enter for sarvmandir] studio

This runner will have the following labels: 'self-hosted', 'Linux', 'X64'
Enter any additional labels (ex. label-1,label-2): [press Enter to skip]

√ Runner successfully added
√ Runner connection is good

# Runner settings

Enter name of work folder: [press Enter for _work]

√ Settings Saved.

studio@sarvmandir:~/actions-runner$ ls
_diag                                    bin        env.sh     run-helper.cmd.template  run.sh         svc.sh
actions-runner-linux-x64-2.319.1.tar.gz  config.sh  externals  run-helper.sh.template   safe_sleep.sh

now use

./run.sh

when you give command ./run.sh your your github runner will become idle.

*example

studio@sarvmandir:~/actions-runner$ ./run.sh

√ Connected to GitHub

Current runner version: '2.319.1'
2024-08-31 06:50:28Z: Listening for Jobs

checkrunner

but use of "./run.sh" will run temporally so we have to listen every time when you add some commit to your main branch repositories.

follow these commands for connections every time with your GitHub repositories.

sudo ./svc.sh install

sudo ./svc.sh start

for checking or getting status or may want to stop the runner so use.

sudo ./svc.sh stop

sudo ./svc.sh status

Step 7: Configure For Ci-Cd

Now set up Ci-Cd when you commit some thing then your vps will build the project using nodjs workflow we write something in your project repositories, making a .workflow/node.yaml file

nodegithubActionsnodejs.yaml file

let's update this file and commit it, we change these followings'

  • remove pull request with its indentation.

  • change in runs-on: ubuntu-latest to self-hosted.

  • add env which taking env values where you setup in secrets and variable.

  • set a fix node js version like node-version:[20.17.0].

  • remove npm test.

given code this is for yarn

name: Node.js CI

on:
  push:
    branches: [ "main" ]

jobs:
  build:
    runs-on: self-hosted
    strategy:
      matrix:
        node-version: [20.17.0]

    env:
      BASE_URL: ${{ secrets.BASE_URL }}
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'yarn'
    - name: Install Dependencies
      run: yarn install --frozen-lockfile
    - name: create .env file
      run: echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env
    - name: Build Project
      run: yarn build

given code this is for npm user

name: Node.js CI

on:
  push:
    branches: [ "main" ]

jobs:
  build:
    runs-on: self-hosted
    strategy:
      matrix:
        node-version: [20.17.0]

    env:
      BASE_URL: ${{ secrets.BASE_URL }}
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - name: Install Dependencies
      run: npm ci
    - name: create .env file
      run: echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env
    - name: Build Project
      run: npm run build

    - name: create .env file
      run: echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env // this .env means saving file name in your vps file with .env you can use .env.local as well.

above code snippet is use if you did not getting env value in your project means in your vps server you can add the echo statement to your nodejs.yaml file. i personally encounter error while i use Prisma and PrismaOrm will not take database env value while other values are take simply as well.

After writing this code commit in your main branch . then go to actions check build status. please ensure your project may have not any errors in building if you have any errors resolve first to check any errors enter this command in your local computer or in vs code

yarn build

final changes in

after completing jobs your repositories will come to your vps and having a build file,to host your project in vps server.

*example:

studio@sarvmandir:~$ cd actions-runner/
studio@sarvmandir:~/actions-runner$ ls
_diag                                    bin        externals                run-helper.sh.template  svc.sh
_work                                    config.sh  run-helper.cmd.template  run.sh
actions-runner-linux-x64-2.319.1.tar.gz  env.sh     run-helper.sh            safe_sleep.sh
studio@sarvmandir:~/actions-runner$ cd _work/
studio@sarvmandir:~/actions-runner/_work$ ls
Code-Craft-Studio  _PipelineMapping  _actions  _temp  _tool
studio@sarvmandir:~/actions-runner/_work$ cd Code-Craft-Studio/Code-Craft-Studio/
studio@sarvmandir:~/actions-runner/_work/Code-Craft-Studio/Code-Craft-Studio$ LS
LS: command not found
studio@sarvmandir:~/actions-runner/_work/Code-Craft-Studio/Code-Craft-Studio$ ls
LICENSE    next-env.d.ts    node_modules       package.json        public  tailwind.config.ts  yarn.lock
README.md  next.config.mjs  package-lock.json  postcss.config.mjs  src     tsconfig.json
studio@sarvmandir:~/actions-runner/_work/Code-Craft-Studio/Code-Craft-Studio$

now add pm2 server for every time your project will running.

step 8:PM2

what is pM2 ?

PM2 is an advanced process manager for NodeJS applications that allows you quickly start, control, or stop your node processes.

Now run your project with pm2

pm2 start --name "your project name" -- start --watch

*example

studio@sarvmandir:~/actions-runner/_work/studio/studio$  pm2 start --name "studio" -- start --watch
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ studio             │ fork     │ 0    │ online    │ 0%       │ 32.7mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

Now paste command:

pm2 startup

After this you get a url which you have to copy and paste,But this command configures PM2 to start automatically at boot using systemd. It ensures that the correct Node.js version (managed by NVM) is used, and sets PM2 to run as the studio (your project name) user.

*example

pm2 startup
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/home/sarvmandir/.nvm/versions/node/v20.17.0/bin /home/stuido/.nvm/versions/node/v20.17.0/lib/node_modules/pm2/bin/pm2 startup systemd -u sarvmandir --hp /home/sarvmandir 

freeze the process via using this command;

pm2 save

check multiple pm2 use:

pm2 list

for checking the logs

pm2 logs

Now check the pm2 is working correctly or not

sudo shutdown

start your VPS server from provider and see the your page is working correctly or not via.

http://your vps ipv4:port number

example = http://192.163.0.9:3000

Last pm2 step where you have to write again code in nodjs.yaml file

    - name: Change directory
      run: cd /home/action-runner/_work/your-repo-name/your-repo-name
    - run: pm2 restart 0

This 0 indcate your pm2 list index

or you can give name your project name also

    - name: Change directory
      run: cd /home/action-runner/_work/your-repo-name/your-repo-name
    - run: pm2 restart studio

*example

name: Node.js CI

on:
  push:
    branches: [ "main" ]

jobs:
  build:
    runs-on: self-hosted
    strategy:
      matrix:
        node-version: [20.17.0]

    env:
      BASE_URL: ${{ secrets.BASE_URL }}
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - name: Install Dependencies
      run: npm ci
    - name: create .env file
      run: echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env
    - name: Build Project
      run: npm run build
    - name: Change directory
      run: cd /home/action-runner/_work/your-repo-name/your-repo-name
    - run: pm2 restart 0

After committing this check all actions process will completely correctly and have no errors.

Step 9:Now Add and a point a domain name

  1. Add DNS to your project domain from your provider.

adddns

*this image is for example do it your own. type A is mandatory and add both domain or www.domain.com to point on your iP runs your server do not add port here only ipv4

Let's give a domain name to your project.

Install Nginx

What Is NGINX?

NGINX is open-source web server software used for reverse proxy, load balancing, and caching. It provides HTTPS server capabilities and is mainly designed for maximum performance and stability. It also functions as a proxy server for email communications protocols, such as IMAP, POP3, and SMTP. 

sudo apt update

sudo apt install nginx

Check Nginx status with this command

systemctl status nginx

now if you go to your domain name it will show Nginx message like this

nginxmessage

Reverse Proxy through nginx

sudo nano /etc/nginx/sites-available/example.com

*example

sudo nano /etc/nginx/sites-available/studio.com

Above command will open nano editor where you have to write some configurations.

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://localhost:3000;
        include proxy_params;
    }
}

Now use Ctrl+X and Ctrl+Y to save the above file

sudo ln -s /etc/nginx/sites-available/examle.com /etc/nginx/sites-enabled/

This creates a symbolic link (shortcut) to an Nginx configuration file (example.com) from the sites-available directory into the sites-enabled directory, enabling the site in Nginx.

Breakdown:

  • ln -s: Creates a symbolic link (a shortcut).

  • /etc/nginx/sites-available/example.com: The original configuration file located in the sites-available directory.

  • /etc/nginx/sites-enabled/: The directory where the symbolic link is placed, effectively enabling the site in Nginx.

sudo nginx -t

this command will show your nginx configurations is correct or not.

sudo systemctl restart nginx

this command will restart your nginx, now you can visit your domain name and it will open your project but without ssl,

Step 10: Add SSL to domain

Install certbot this bot will take ssl

sudo apt install certbot python3-certbot-nginx

again check configurations of nginx

sudo nginx -t

Give request to certbot to take ssl.

sudo certbot --nginx -d example.com -d www.example.com

This command uses Certbot to automatically obtain and install an SSL/TLS certificate for example.com and www.example.com with Nginx.

Breakdown:

  • sudo: Runs the command with superuser (root) privileges, necessary for modifying Nginx configuration and installing certificates.

  • certbot: The tool for automatically obtaining and renewing SSL/TLS certificates from Let's Encrypt.

  • --nginx: Tells Certbot to use the Nginx plugin, which will automatically configure SSL for the specified domains in your Nginx configuration.

  • -d example.com -d www.example.com: Specifies the domains for which to obtain the certificate.

What it does:

  • Obtains a free SSL/TLS certificate from Let's Encrypt for example.com and www.example.com.

Conclusion

This is complete beginner solutions for deploy your first or nth production projects in this blog we'll discuss about core concept of cicd,pm2,ssl and some ubuntu or linux basics command,after this your are good to go to host your projects easily and have access of code to copy paste in your terminal. here are complete step by step command is providing you to deploy your project , in next blog we will dissuades all hosting related to error because yo may encounter more and more errors while hosting your production code,still you get any error we are here to solve your error ,write your comment with your error we'll solve it, if you find helpful this blog please like and comment and share .

ASHISH Gupta

ASHISH Gupta

Creative, Elegant and Visionary

Latest

from the blog

The latest industry news,interviews and resources

Python's One-Line if else Magic

Python's One-Line if else Magic

Python's One-Line if else Magic

Best Alternatives to Adobe's Generative Fill AI Free

Best Alternatives to Adobe's Generative Fill AI Free

Fill for free online which enhance and boost your design works

Join our newsletter

we'll send you a nice letter once per week. No spam.

BiyondBytes

© 2024 BiyondBytes. All rights reserved.