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:
cd /var/www/Step 1.2
Now, create a folder inside /var/www/ and name should be: project-name or projectname (LowerCase)
mkdir project-nameNow, go to the created folder:
cd project-nameNow, 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.
mkdir -p data scripts webStep 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:
ls -la ~/.sshIf you don't have a key, generate one and just press enter:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "your-email@example.com"Start SSH Agent:
eval "$(ssh-agent -s)"Add Key to SSH Agent
ssh-add ~/.ssh/id_ed25519Copy public key output:
cat ~/.ssh/id_ed25519.pubAdd 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:
ssh-keyscan github.com >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hostsTest the connection:
ssh -T git@github.comExpected:
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:
cd /var/www/project-name/web/Inside web, clone the repo:
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:
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:
sudo docker compose up --build -dIf 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:
nano /etc/caddy/CaddyfileIf Caddyfile is not created then create by:
sudo mkdir -p /etc/caddyAfter this run below command to create and open Caddyfile in nano editor:
sudo nano /etc/caddy/CaddyfileNow, paste the below code config in the Caddyfile: Replace only xyz.com with your actual domain.
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.
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:
sudo systemctl restart caddyNow 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:
mysql -u root -pEnter your MySQL root password (Created when installing MySQL in VPS).
To Generate Secure Password, use below command in your local terminal:
openssl rand -hex 20Now, copy and run the below command to create database and user. Note: replace your actual values.
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.
docker exec -it container_name_app npx drizzle-kit migrateOne last step: if you have any script which creates first time admin login credentials then run below command:
docker exec container_name_app npm run script_command_nameyou will see something: migration applied successfully. Now all steps are done. Your Website is now fully up and running.