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.
Mục lục bài viết
VPS & Hosting cho dự án của bạn
Khám phá các gói máy chủ và lưu trữ phù hợp từ website cá nhân đến ứng dụng production.
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 cwd là apps/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_SECRETvàJWT_REFRESH_SECRETphải là chuỗi dài, random, không dùng giá trị trong.env.example.CORS_ORIGINSphải chứa domain user app và admin app.API_PREFIX=/apinghĩ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/.envtồ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.productionlúc build. - Build trước khi sửa
NEXT_PUBLIC_API_BASE_URLhoặcVITE_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
remapvà userremapđã tạo. - Source code đã clone vào
/var/www/remap. -
npm installthà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:buildpass. -
npm run user:buildpass. -
npm run admin:buildpass. - 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/healthtrả status ok. - User app mở được bằng HTTPS.
- Admin app mở được bằng HTTPS.
- CORS cho phép user/admin domain.
- PM2 đã
savevà cấu hình startup. - Có quy trình backup database.
Quy trình làm lại nếu cần
- Đọ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.
- 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.
- 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.
- 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.
- 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.
- 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
Bài viết liên quan
Mới mua VPS Ubuntu cần làm gì? Checklist cấu hình production
Checklist cấu hình VPS Ubuntu mới gồm update, user sudo, SSH key, UFW, timezone, swap, log, backup và kiểm tra bảo mật.
Đọc bài →Cách deploy Next.js lên VPS Ubuntu
Hướng dẫn build và chạy ứng dụng Next.js production bằng PM2, Nginx, domain và HTTPS trên VPS Ubuntu theo từng bước kiểm tra được.
Đọc bài →VPS là gì? Cách hoạt động, ưu nhược điểm và cách chọn VPS
Tìm hiểu VPS là gì, cách máy chủ ảo hoạt động, VPS khác hosting và server riêng thế nào, cùng checklist chọn cấu hình phù hợp.
Đọc bài →