Documentation/VPS Deployment/Deploy Next.js App to VPS
Last updated June 20, 2026

Deploy Next.js to VPS Step by Step

This guide presumes that your next.js stack is:

  • Next.js 16
  • Bun
  • Drizzle ORM
  • MySQL database
  • Docker
  • Github Repo
  • Caddy Web Server
  • VPS
  • Domain

Step 1:

Go to your vps terminal and create your project folder structure.

Step 1.1

First, go to this directory:

bash
cd /var/www/

Step 1.2

Now, create a folder inside /var/www/ and name should be: project-name or projectname (LowerCase)

bash
mkdir project-name

Now, go to the created folder:

bash
cd project-name

Now, inside this folder, we have to create 3 folders which will seperate our code, data and scripts which prevents data loss every time source code updates via git pull on github actions.

bash
mkdir -p data scripts web

Step 2:

Now, we have to connect our vps to our github in order to clone our project repo in our created folder inside web directory: /var/www/project-name/web/

Step 2.1

Generate an SSH key on the VPS:

Check whether one already exists:

bash
ls -la ~/.ssh

If you don't have a key, generate one and just press enter:

bash
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "your-email@example.com"

Start SSH Agent:

bash
eval "$(ssh-agent -s)"

Add Key to SSH Agent

bash
ssh-add ~/.ssh/id_ed25519

Copy public key output:

bash
cat ~/.ssh/id_ed25519.pub

Add this public key to Github Account Level: GitHub → Settings → SSH and GPG keys → New SSH key Give any title like: ssh-vps-key and paste public key without any extra spaces and click Add SSH key button.

Trust Github Host in VPS:

bash
ssh-keyscan github.com >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts

Test the connection:

bash
ssh -T git@github.com

Expected:

Hi username! You've successfully authenticated, but GitHub does not provide shell access.

Now, your github account is conneted with your vps.

Step 3:

Now, we have to go to our web folder that we created earlier:

bash
cd /var/www/project-name/web/

Inside web, clone the repo:

bash
git clone git@github.com:username/repo-name.git .

Note: here, dot (.) clones the repo in current folder, otherwise it will create its own folder inside web dir from the repo name. So, we don't want to make another folder inside web, so we add dot in the end.

Step 4:

Go to web folder again:

bash
cd /var/www/project-name/web/

Now, inside web/ run the following command to build and run the docker image and containers as defined in docker-compose.yml file:

bash
sudo docker compose up --build -d

If build success, you will see example output something like:

[+] Building 25.3s => Building web => Pulling postgres [+] Running 3/3 ✔ Network myapp_default Created ✔ Container myapp-db Started ✔ Container myapp-web Started

Step 5:

Setup Domain DNS Settings:

Go to your Hosting Account, find Domains Section and click Manage DNS: Add New Records one by one:

First, we add A Type Record:

  • Type: A
  • Name: @
  • Priority: 0
  • Content: your_vps_ip
  • TTL: 300

Second, we add CNAME Type record:

  • Type: CNAME
  • Name: www
  • Priority: 0
  • Content: your_domain_name -> xyz.com
  • TTL: 300

Note: if some records are already present then edit and replace with these exact records.

If you want to deploy to your sub domain then just add one more DNS Record below:

  • Type: A
  • Name: your_sub_domain -> ecom
  • Priority: 0
  • Content: your_vps_ip
  • TTL: 300

This will create your subdomain: ecom.xyz.com

Step 6:

Configure Caddy:

Now, we will configure Caddy Web Server in order to serve your website traffic:

Step 6.1

Open Caddyfile in nano editor:

bash
nano /etc/caddy/Caddyfile

If Caddyfile is not created then create by:

bash
sudo mkdir -p /etc/caddy

After this run below command to create and open Caddyfile in nano editor:

bash
sudo nano /etc/caddy/Caddyfile

Now, paste the below code config in the Caddyfile: Replace only xyz.com with your actual domain.

bash
xyz.com {
   reverse_proxy 127.0.0.1:3000 {
       header_up X-Forwarded-Proto {scheme}
       header_up X-Forwarded-Host {host}
       header_up Host {host}
   }
}

And if you want your website to run on subdomain that you created earlier then only paste below code (Do not paste above code in case of subdomain). Replace with your actual subdomain.

bash
ecom.xyz.com {
   reverse_proxy 127.0.0.1:3001 {
       header_up X-Forwarded-Proto {scheme}
       header_up X-Forwarded-Host {host}
       header_up Host {host}
   }
}

Now press CTRL + O then press ENTER then press CTRL + X

After this, Restart Caddy Service:

bash
sudo systemctl restart caddy

Now check your domain or sub-domain, your website is up and running. And if your website needs database to run then Continue to Step 7.

Step 7:

Configuring Database:

First, we have to login into our vps root then run below command:

bash
mysql -u root -p

Enter your MySQL root password (Created when installing MySQL in VPS).

To Generate Secure Password, use below command in your local terminal:

bash
openssl rand -hex 20

Now, copy and run the below command to create database and user. Note: replace your actual values.

bash
CREATE DATABASE database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'user_name'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('your_password');
GRANT ALL PRIVILEGES ON database_name.* TO 'user_name'@'%';
FLUSH PRIVILEGES;
EXIT;

Now, run below command to migrate tables into database.

bash
docker exec -it container_name_app npx drizzle-kit migrate

One last step: if you have any script which creates first time admin login credentials then run below command:

bash
docker exec container_name_app npm run script_command_name

you will see something: migration applied successfully. Now all steps are done. Your Website is now fully up and running.