
Building Your Portfolio Experience
Loading Tariqul Islam's projects...
Loading Tariqul Islam's projects...
This guide helps you deploy an Express.js project (JavaScript or TypeScript) on a Hostinger VPS using Ubuntu, PM2, NGINX, and Let’s Encrypt HTTPS — with routing via a subdomain (e.g. `api.domain-name.com`) while keeping your main domain (`domain-name.com`) available.
Hostinger VPS (Ubuntu 22.04+)
Domain connected to VPS
GitHub repo with your Express.js project
Node.js v20+ and NGINX installed
ssh root@your-vps-ip
sudo apt update && sudo apt upgrade -y
# Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Nginx
sudo apt install nginx -y
sudo systemctl enable nginx
sudo service nginx status
# PM2
sudo npm install -g pm2
pm2 startup
# Git
sudo apt install git -y
# Certbot
sudo apt install python3-certbot-nginx -y
# Firewall
sudo ufw enable
sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH
At your domain provider, configure A/AAAA
records:
Type | Name | Value |
---|---|---|
A | @ | VPS IPv4 |
A | www | VPS IPv4 |
AAAA | @ | VPS IPv6 |
AAAA | www | VPS IPv6 |
> Note: Add A
record for api subdomain to route traffic to your Express.js app. IPv6 AAAA
records) are optional and should only be added if your VPS provider supports and assigns an IPv6 address.
mkdir -p ~/apps && cd ~/apps
git clone git@github.com:yourusername/your-repo.git
cd your-repo
If private repo:
ssh-keygen -t ed25519 -C "your_email@example.com"
cat ~/.ssh/id_ed25519.pub
Add this key to GitHub > Settings > Deploy Keys.
Delete default config:
sudo rm /etc/nginx/sites-enabled/default
domain-name.com/api
)Create config:
sudo nano /etc/nginx/sites-available/domain-name.com
Paste:
server {
listen 80;
server_name api.domain-name.com;
location / {
proxy_pass http://localhost:3000;
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;
}
}
api.domain-name.com
)Create config:
sudo nano /etc/nginx/sites-available/api.domain-name.com
Paste:
server {
listen 80;
server_name api.domain-name.com;
location / {
proxy_pass http://localhost:3000;
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;
}
}
Enable it:
sudo ln -s /etc/nginx/sites-available/domain-name.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/api.domain-name.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
cd ~/apps/your-repo
nano .env # if needed
npm install
npm run build # if using TypeScript or Prisma
npm run prisma:generate # if using Prisma
npm run prisma:migrate # if using Prisma
nano ecosystem.config.js
TypeScript Example:
module.exports = {
apps: [
{
name: 'express-app',
script: 'dist/server.js',
env: {
NODE_ENV: 'production',
},
},
],
};
JavaScript Example:
module.exports = {
apps: [
{
name: 'express-app',
script: 'src/server.js',
env: {
NODE_ENV: 'production',
},
},
],
};
Start and save:
pm2 start ecosystem.config.js
pm2 save
pm2 status
sudo certbot --nginx -d domain-name.com -d www.domain-name.com
sudo certbot --nginx -d api.domain-name.com
sudo nginx -t && sudo systemctl reload nginx
sudo systemctl enable certbot.timer
sudo certbot renew --dry-run
cd ~/apps/your-repo
git pull origin main
npm install
npm run build
pm2 reload express-app
Your Express.js app is now live with HTTPS, running via PM2, and served by Nginx.
You can access it at:
Happy deploying! 🚀