DeployEasy
Menu
VPSNâng cao

Hướng dẫn deploy REMAP lên VPS Ubuntu

Triển khai monorepo REMAP gồm NestJS, Next.js, React và PostgreSQL lên VPS Ubuntu bằng PM2, Nginx, domain và chứng chỉ SSL.

· 9 phút đọc · 1.969 từ
Mục lục bài viết

Tài liệu này hướng dẫn deploy project REMAP lên VPS Ubuntu, gồm:

  • Backend API: NestJS, workspace @remap/api, port nội bộ 3000
  • User frontend: Next.js, workspace @remap/user, port nội bộ 3001
  • Admin frontend: React + Vite static build, workspace @remap/admin
  • Database: PostgreSQL
  • Process manager: PM2
  • Reverse proxy và SSL: Nginx + Certbot

Ví dụ domain trong tài liệu:

User app:  https://remap.io.vn
Admin app: https://admin.remap.io.vn
API:       https://api.remap.io.vn/api

Khi deploy thật, domain production đã dùng trong tài liệu là remap.io.vn; thay password, secret, IP VPS và Git repo bằng giá trị thật.


1. Chuẩn bị VPS

SSH vào VPS:

ssh root@YOUR_SERVER_IP

Cập nhật hệ thống:

apt update && apt upgrade -y

Cài package cần thiết:

apt install -y git curl nginx ufw postgresql postgresql-contrib

Mở firewall cơ bản:

ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
ufw status

2. Cài Node.js và PM2

Project đang dùng Node/NPM workspace. Khuyến nghị Node.js 22 LTS.

curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt install -y nodejs
node -v
npm -v

Cài PM2:

npm install -g pm2
pm2 -v

3. Tạo database PostgreSQL

Đăng nhập user postgres:

sudo -u postgres psql

Tạo user và database:

CREATE USER remap WITH PASSWORD 'CHANGE_ME_DB_PASSWORD';
CREATE DATABASE remap OWNER remap;
GRANT ALL PRIVILEGES ON DATABASE remap TO remap;
\q

Kiểm tra kết nối:

psql postgresql://remap:CHANGE_ME_DB_PASSWORD@localhost:5432/remap

Thoát psql bằng:

\q

4. Clone source code

Tạo thư mục deploy:

mkdir -p /var/www
cd /var/www

Clone repo:

git clone YOUR_GITHUB_REPO_URL remap
cd remap

Cài dependencies cho toàn bộ monorepo:

npm install

Project dùng npm workspaces:

apps/api    -> @remap/api
apps/user   -> @remap/user
apps/admin  -> @remap/admin
packages/*  -> shared packages

5. Cấu hình biến môi trường backend API

Backend đọc env từ file .env theo current working directory. Khi chạy PM2, nên đặt cwdapps/api, vì vậy file production env nên nằm tại:

/var/www/remap/apps/api/.env

Tạo file:

nano apps/api/.env

Nội dung mẫu production:

NODE_ENV=production
API_PORT=3000
API_PREFIX=/api

DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=remap
DATABASE_PASSWORD=CHANGE_ME_DB_PASSWORD
DATABASE_NAME=remap

JWT_ACCESS_SECRET=CHANGE_ME_LONG_RANDOM_ACCESS_SECRET
JWT_REFRESH_SECRET=CHANGE_ME_LONG_RANDOM_REFRESH_SECRET
JWT_ACCESS_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d

CORS_ORIGINS=https://remap.io.vn,https://admin.remap.io.vn

Lưu ý:

  • JWT_ACCESS_SECRETJWT_REFRESH_SECRET phải là chuỗi dài, random, không dùng giá trị trong .env.example.
  • CORS_ORIGINS phải chứa domain user app và admin app.
  • API_PREFIX=/api nghĩa là health check nằm ở /api/health.

6. Cấu hình biến môi trường frontend

User app Next.js

User app dùng biến:

NEXT_PUBLIC_API_BASE_URL

Tạo file:

nano apps/user/.env.production

Nội dung:

NEXT_PUBLIC_API_BASE_URL=https://api.remap.io.vn/api

Biến NEXT_PUBLIC_* được Next.js đưa vào bundle lúc build, nên nếu đổi API URL thì phải build lại user app.

Admin app Vite

Admin app dùng biến:

VITE_API_BASE_URL

Tạo file:

nano apps/admin/.env.production

Nội dung:

VITE_API_BASE_URL=https://api.remap.io.vn/api

Biến VITE_* cũng được đưa vào bundle lúc build, nên đổi API URL thì phải build lại admin app.


7. Build project

Từ root repo:

cd /var/www/remap

Kiểm tra type/build backend:

npm run api:typecheck
npm run api:build

Kiểm tra và build frontend:

npm run frontend:typecheck
npm run user:build
npm run admin:build

Sau build:

  • Backend build ra apps/api/dist
  • User Next.js build ra apps/user/.next
  • Admin Vite build ra apps/admin/dist

8. Chạy migration database

Migration TypeORM hiện chạy bằng typeorm-ts-node-commonjs, đọc env từ process env và file .env khi dùng cwd phù hợp.

Chạy từ workspace API:

cd /var/www/remap/apps/api
npm run migration:run
npm run migration:show

Hoặc từ root:

cd /var/www/remap
npm run -w @remap/api migration:run
npm run -w @remap/api migration:show

Nếu chạy từ root mà env không được đọc đúng, hãy export biến môi trường hoặc chạy trong apps/api như cách đầu tiên.


9. Chạy backend API bằng PM2

Chạy API từ thư mục apps/api để Nest đọc đúng file .env:

cd /var/www/remap/apps/api
pm2 start npm --name remap-api -- start

Lệnh npm start của @remap/api sẽ chạy:

node dist/main.js

Kiểm tra:

pm2 status
pm2 logs remap-api
curl http://127.0.0.1:3000/api/health

Kỳ vọng health check:

{"status":"ok","service":"api"}

10. Chạy user app Next.js bằng PM2

User app Next.js cần process Node riêng ở port 3001.

cd /var/www/remap/apps/user
pm2 start npm --name remap-user -- start

Kiểm tra:

pm2 logs remap-user
curl -I http://127.0.0.1:3001

11. Deploy admin app Vite static

Admin app là static build trong:

/var/www/remap/apps/admin/dist

Không cần PM2 cho admin app nếu serve bằng Nginx static.

Kiểm tra file build:

ls -la /var/www/remap/apps/admin/dist

Thư mục này sẽ được Nginx serve trực tiếp ở https://admin.remap.io.vn.


12. Lưu PM2 và auto-start khi reboot

pm2 save
pm2 startup

PM2 sẽ in ra một lệnh systemd. Copy lệnh đó và chạy lại một lần.

Kiểm tra:

pm2 status

13. Cấu hình Nginx reverse proxy

Xóa default site nếu cần:

rm -f /etc/nginx/sites-enabled/default

Tạo config:

nano /etc/nginx/sites-available/remap

Nội dung mẫu:

server {
    listen 80;
    server_name api.remap.io.vn;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

server {
    listen 80;
    server_name remap.io.vn www.remap.io.vn;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
    }
}

server {
    listen 80;
    server_name admin.remap.io.vn;

    root /var/www/remap/apps/admin/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /assets/ {
        try_files $uri =404;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

Kích hoạt site:

ln -s /etc/nginx/sites-available/remap /etc/nginx/sites-enabled/remap
nginx -t
systemctl reload nginx

Kiểm tra:

curl -I http://api.remap.io.vn/api/health
curl -I http://remap.io.vn
curl -I http://admin.remap.io.vn

14. Trỏ DNS về VPS

Tạo các bản ghi DNS:

Type: A
Name: api
Value: YOUR_SERVER_IP

Type: A
Name: @
Value: YOUR_SERVER_IP

Type: A
Name: www
Value: YOUR_SERVER_IP

Type: A
Name: admin
Value: YOUR_SERVER_IP

Chờ DNS cập nhật, kiểm tra:

ping api.remap.io.vn
ping remap.io.vn
ping admin.remap.io.vn

15. Cài SSL HTTPS bằng Certbot

Cài Certbot:

apt install -y certbot python3-certbot-nginx

Cấp SSL cho cả 3 domain:

certbot --nginx \
  -d api.remap.io.vn \
  -d remap.io.vn \
  -d www.remap.io.vn \
  -d admin.remap.io.vn

Kiểm tra auto renew:

certbot renew --dry-run

Sau khi có HTTPS, kiểm tra lại:

curl https://api.remap.io.vn/api/health

16. Smoke test production

Từ VPS hoặc máy local, kiểm tra health:

curl https://api.remap.io.vn/api/health

Nếu muốn chạy smoke test against production API:

cd /var/www/remap
API_BASE_URL=https://api.remap.io.vn/api npm run -w @remap/api test:auth
API_BASE_URL=https://api.remap.io.vn/api npm run -w @remap/api test:property
API_BASE_URL=https://api.remap.io.vn/api npm run -w @remap/api test:favorites

Lưu ý:

  • Smoke test tạo user/property/favorite random và không cleanup.
  • Chỉ nên chạy trên staging hoặc production khi chấp nhận tạo dữ liệu test.

Kiểm tra UI:

https://remap.io.vn
https://admin.remap.io.vn

17. Quy trình deploy lại khi có code mới

SSH vào VPS:

ssh root@YOUR_SERVER_IP
cd /var/www/remap

Pull code:

git pull origin main

Cài lại dependencies nếu package-lock.json hoặc package thay đổi:

npm install

Build lại:

npm run api:build
npm run user:build
npm run admin:build

Chạy migration nếu có migration mới:

cd /var/www/remap/apps/api
npm run migration:run

Restart process:

pm2 restart remap-api
pm2 restart remap-user
pm2 status

Admin app là static file, chỉ cần build lại apps/admin/dist; Nginx sẽ serve bản mới.

Reload Nginx nếu có đổi config:

nginx -t
systemctl reload nginx

18. Lệnh deploy nhanh

Sau lần deploy đầu tiên, thường dùng:

cd /var/www/remap
git pull origin main
npm install
npm run api:build
npm run user:build
npm run admin:build
cd apps/api && npm run migration:run && cd ../..
pm2 restart remap-api
pm2 restart remap-user
pm2 status

Nếu không có migration mới, có thể bỏ qua dòng migration:run.


19. Backup và restore database

Backup:

pg_dump -U remap -h localhost -d remap > /var/backups/remap-$(date +%F).sql

Restore vào database rỗng:

psql -U remap -h localhost -d remap < /var/backups/remap-YYYY-MM-DD.sql

Nên cấu hình cron backup riêng cho production.


20. Lỗi thường gặp

20.1 API không đọc được .env

Dấu hiệu:

  • API kết nối sai database.
  • API dùng secret fallback.
  • Port không đúng.

Cách xử lý:

  • Chạy PM2 với cwd là /var/www/remap/apps/api.
  • Đảm bảo file /var/www/remap/apps/api/.env tồn tại.
  • Kiểm tra pm2 describe remap-api.

20.2 API lỗi database connection

Kiểm tra PostgreSQL:

systemctl status postgresql
psql postgresql://remap:CHANGE_ME_DB_PASSWORD@localhost:5432/remap

Kiểm tra env:

cat /var/www/remap/apps/api/.env

20.3 Migration không chạy

Chạy trong thư mục API:

cd /var/www/remap/apps/api
npm run migration:show
npm run migration:run

Nếu lỗi enum/table đã tồn tại, kiểm tra database có phải database sạch hay đã từng chạy migration một phần.

20.4 User/Admin gọi API bị CORS

Kiểm tra CORS_ORIGINS trong apps/api/.env:

CORS_ORIGINS=https://remap.io.vn,https://admin.remap.io.vn

Restart API sau khi sửa env:

pm2 restart remap-api

20.5 Frontend vẫn gọi API localhost

Nguyên nhân:

  • Thiếu .env.production lúc build.
  • Build trước khi sửa NEXT_PUBLIC_API_BASE_URL hoặc VITE_API_BASE_URL.

Cách xử lý:

npm run user:build
npm run admin:build
pm2 restart remap-user

20.6 Admin route refresh bị 404

Admin app là SPA Vite. Nginx cần có:

try_files $uri $uri/ /index.html;

20.7 Port bị chiếm

Kiểm tra:

lsof -i :3000
lsof -i :3001

Kiểm tra PM2:

pm2 status
pm2 logs remap-api
pm2 logs remap-user

21. Checklist hoàn thành

  • VPS SSH được.
  • Node.js và npm đã cài.
  • PM2 đã cài.
  • PostgreSQL đã cài.
  • Database remap và user remap đã tạo.
  • Source code đã clone vào /var/www/remap.
  • npm install thành công.
  • apps/api/.env đã cấu hình production.
  • apps/user/.env.production đã cấu hình API URL HTTPS.
  • apps/admin/.env.production đã cấu hình API URL HTTPS.
  • npm run api:build pass.
  • npm run user:build pass.
  • npm run admin:build pass.
  • Migration đã chạy thành công.
  • PM2 đang chạy remap-api.
  • PM2 đang chạy remap-user.
  • Nginx serve admin static từ apps/admin/dist.
  • Nginx proxy API về port 3000.
  • Nginx proxy user app về port 3001.
  • Domain api, admin, user app đã trỏ về VPS.
  • SSL HTTPS đã cấp bằng Certbot.
  • https://api.remap.io.vn/api/health trả status ok.
  • User app mở được bằng HTTPS.
  • Admin app mở được bằng HTTPS.
  • CORS cho phép user/admin domain.
  • PM2 đã save và cấu hình startup.
  • Có quy trình backup database.

Quy trình làm lại nếu cần

  1. Đọc lại phần mục tiêu, phạm vi và điều kiện hoàn thành của tài liệu này.
  2. Kiểm tra các file code, cấu hình hoặc tài liệu liên quan được nhắc trong nội dung.
  3. Thực hiện lại từng bước theo đúng thứ tự đã mô tả, tránh bỏ qua bước chuẩn bị môi trường hoặc migration.
  4. Chạy các lệnh kiểm tra phù hợp với phạm vi thay đổi, ưu tiên các lệnh đã ghi trong phần kết quả verify hoặc checklist.
  5. Nếu phát hiện khác biệt so với tài liệu, cập nhật lại phần kết quả triển khai, checklist và ghi chú lỗi thường gặp.
  6. Khi hoàn tất, kiểm tra git status để đảm bảo chỉ các file đúng phạm vi được thay đổi.

Đọc tiếp