Deploy Docs Site (PM2 + Nginx)
Hướng dẫn deploy documents.<domain> — static HTML được build từ
docs/**/*.md qua scripts/build.mjs, chạy 24/7 bằng PM2, nginx làm reverse
proxy + TLS.
Xem ../scripts/README.md cho chi tiết build pipeline.
Prerequisites trên VPS
# Node 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Yarn classic (project dùng Yarn 1)
sudo npm i -g yarn pm2
# Nginx + certbot (nếu chưa có)
sudo apt install -y nginx certbot python3-certbot-nginx
PM2 đã được dùng cho booking-api + booking-web ở dự án này, em không
cần cài lại nếu VPS đang chạy 2 app đó.
Lần đầu: clone + build + start
# 1. Clone repo docs (repo riêng với booking-api/web)
cd /var/www
git clone git@github.com:novagoo/booking-docs.git
cd booking-docs
# 2. Cài deps + build ra dist/
yarn install --frozen-lockfile
yarn build
# 3. Khởi PM2
pm2 start ecosystem.config.cjs
pm2 save # persist process list qua reboot
# 4. Lần đầu dùng PM2 trên máy — sinh systemd unit để auto-start on boot
pm2 startup systemd
# Copy-paste dòng `sudo env PATH=... pm2 startup ...` PM2 in ra rồi chạy
# 5. Verify
pm2 status # thấy `booking-docs` online
curl -I http://127.0.0.1:4010/ # 200 OK
Nginx reverse proxy
Tạo /etc/nginx/sites-available/booking-docs:
server {
listen 80;
server_name documents.salon.no;
# Certbot sẽ tự thêm block TLS bên dưới khi chạy lệnh dưới cùng
location / {
proxy_pass http://127.0.0.1:4010;
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;
}
# Gzip — các file HTML/CSS/JS static nén rất tốt
gzip on;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
}
sudo ln -s /etc/nginx/sites-available/booking-docs /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# TLS
sudo certbot --nginx -d documents.salon.no
Alternative: Nginx serve dist/ trực tiếp (không PM2)
Nếu VPS quá chật RAM (không muốn tốn thêm Node process), có thể bỏ PM2 hoàn
toàn — nginx serve thẳng file dist/. Trade-off: mất khả năng
pm2 logs booking-docs, mỗi lần deploy phải yarn build trên VPS (hoặc
rsync dist/ từ local).
server {
listen 80;
server_name documents.salon.no;
root /var/www/booking-docs/dist;
index index.html;
# Clean URLs: /flows/payment-flow → /flows/payment-flow.html
location / {
try_files $uri $uri/ $uri.html =404;
}
location ~* \.(css|js|woff2?|svg|png|jpg|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Deploy update (sau khi docs có thay đổi)
ssh vps
cd /var/www/booking-docs
git pull
yarn install --frozen-lockfile # skip nếu package.json không đổi
yarn build
pm2 restart booking-docs # pick up new dist/ + restart Node
Hoặc script gọn 1 dòng trên local (không cần ssh thủ công):
ssh deploy@vps 'cd /var/www/booking-docs && git pull && yarn install --frozen-lockfile && yarn build && pm2 restart booking-docs'
Useful PM2 commands
pm2 status # danh sách process
pm2 logs booking-docs # tail logs (Ctrl+C để thoát)
pm2 logs booking-docs --lines 200 # 200 dòng gần nhất
pm2 restart booking-docs # restart
pm2 reload booking-docs # zero-downtime reload (không cần thiết cho static)
pm2 stop booking-docs # stop (giữ trong list)
pm2 delete booking-docs # xóa khỏi list
pm2 monit # dashboard live CPU/memory
pm2 describe booking-docs # config + log paths
pm2 flush booking-docs # xóa log cũ
Log rotation
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
Tránh log file phình to khi chạy nhiều tháng.
Troubleshooting
| Triệu chứng | Nguyên nhân thường gặp | Fix |
|---|---|---|
pm2 status → errored |
Port 4010 đang bị app khác dùng | lsof -i :4010 → đổi DOCS_PORT trong ecosystem.config.cjs rồi pm2 restart |
| 502 Bad Gateway từ nginx | PM2 process chết / port mismatch | pm2 logs booking-docs --lines 100 xem stacktrace |
| Đổi docs xong vẫn thấy bản cũ | Browser cache (production bật max-age=86400 cho CSS/JS) |
Hard refresh (Cmd+Shift+R) hoặc bump query string trong template |
| Sidebar mất section mới thêm | Build chưa chạy / dist/ chưa rsync |
yarn build lại rồi pm2 restart booking-docs |
Page 404 khi click anchor .md |
Link chưa được rewrite (có thể do syntax edge case trong marked) | Check regex trong rewriteMarkdownLinks của scripts/build.mjs |
| Node version quá cũ | Builder dùng ES2022+ + replaceAll |
Yêu cầu Node ≥ 20 (xem package.json engines) |