Deploy WELIFE API trên Ubuntu
Snapshot máy chủ hiện tại (2026-07-31)
| Mục | Giá trị |
|---|---|
| Host / IP | 180.93.146.58 (Megahost) |
| OS | Ubuntu 22.04.5 LTS · kernel 5.15.0-156-generic x86_64 |
| SSH | ssh root@180.93.146.58 (nên chuyển sang user welifeadmin + tắt root — § harden) |
| Domain API | https://api.minhtien.cloud · WS cùng host |
| Admin | https://admin.minhtien.cloud |
| Docs | https://docs.minhtien.cloud |
| Apex | https://minhtien.cloud |
Phân vai deploy (quan trọng):
| Thành phần | Chạy ở đâu | Ghi chú |
|---|---|---|
| Nest API + Postgres/Redis/MinIO | VPS (Docker / PM2) | Đây là phần deploy máy chủ |
| Dashboard admin | VPS (static Nginx) | deploy/deploy-dashboard.sh |
| Consumer / Driver / Partner (Expo) | Máy build / EAS / store | App không host trên VPS; chỉ gọi EXPO_PUBLIC_API_URL |
FE mới (2026-07) — cần native build
Trên mobile/ đã có camera · mic · media · voice search (expo-speech-recognition).
- Expo Go không đủ cho STT / một số native module.
- Production / test thiết bị:
npx expo run:android·run:ioshoặc EAS Build. - Env production app:
EXPO_PUBLIC_API_URL=https://api.minhtien.cloud/api/v1
EXPO_PUBLIC_WS_URL=https://api.minhtien.cloud
EXPO_PUBLIC_ENV=production
Kiểm tra nhanh trên VPS (sau SSH)
# API sống (public)
curl -sS https://api.minhtien.cloud/api/v1/health
# /health/deps public → 403 trên production (đúng thiết kế).
# Detail deps: JWT ADMIN + HEALTH_DETAIL_ENABLED=true
# POST /api/v1/auth/... lấy accessToken role ADMIN, rồi:
# curl -sS -H "Authorization: Bearer $TOKEN" \
# https://api.minhtien.cloud/api/v1/health/admin/deps
# Container kỳ vọng: api · postgres · redis · minio · mailpit
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | head -40
bash /root/health-check.sh
Deploy / cập nhật API từ Windows
/opt/welife không phải git clone (repo private). Cập nhật bằng sync từ máy dev (giữ nguyên api/.env + .env trên VPS).
# Option A — sync api + rebuild compose (khuyến nghị)
# Git Bash / WSL từ root repo:
bash deploy/sync-api-vps.sh
# Dashboard admin (static Vite → admin.minhtien.cloud)
bash deploy/sync-dashboard-vps.sh
# Marketing site (Next static → minhtien.cloud + www)
bash deploy/sync-web-vps.sh
# Tài liệu markdown → static HTML (docs.minhtien.cloud)
bash deploy/sync-docs-vps.sh
# Option B — one-shot bootstrap cũ (cần `.vps-pass` gitignored)
node --use-system-ca deploy/push-and-run.mjs
# Option C — SSH tay (sau khi đã sync code)
ssh root@180.93.146.58
cd /opt/welife
docker compose -f docker-compose.yml -f deploy/docker-compose.resources.yml --profile full up -d --build api minio
docker compose -f docker-compose.yml -f deploy/docker-compose.resources.yml --profile full run --rm minio-init
docker compose -f docker-compose.yml -f deploy/docker-compose.resources.yml --profile full exec -T api npx prisma migrate deploy
# OSRM (nếu đã bootstrap data)
docker compose -f docker-compose.yml -f deploy/docker-compose.resources.yml --profile osrm up -d --force-recreate osrm
bash /root/health-check.sh
Luôn kèm -f deploy/docker-compose.resources.yml trên VPS (giới hạn RAM/CPU). deploy/sync-api-vps.sh đã gắn overlay này.
VPS api/.env bắt buộc (sync tự set):
| Key | Giá trị prod |
|---|---|
POSTGIS_SCHEMA |
public (PostGIS extension trên Ubuntu/aaPanel) |
SWAGGER_ENABLED |
true (OWNER 2026-08-14 — /docs public; set WELIFE_SWAGGER_ENABLED=false khi sync để tắt) |
HEALTH_DETAIL_ENABLED |
false (detail chỉ JWT ADMIN khi bật) |
sync-api-vps.sh cũng: strip BOM migration · wait /health · migrate deploy · seed transit_stops nếu trống · smoke transit nearby · Swagger theo WELIFE_SWAGGER_ENABLED (default true từ 2026-08-14) · health-detail/wallet-credit off.
Không git pull trên VPS trừ khi đã cấu hình deploy key riêng.
Không commit .env / secret JWT / SMS / payment lên git.
Trạng thái vận hành (đã xử lý 2026-07-31)
| Lưu ý cũ | Cách xử lý |
|---|---|
| JWT rotate (≥32 ký tự) | Bắt buộc secure-boot prod; user login lại một lần. Backup: api/.env.bak-pre-jwt-rotate |
Không git pull |
Dùng deploy/sync-api-vps.sh |
| MinIO tắt | welife-minio up · bucket welife · API S3_ENDPOINT=http://minio:9000 |
/health/deps 403 |
Public cấm; dùng /health/admin/deps + JWT ADMIN (HEALTH_DETAIL_ENABLED=true) |
| SMTP trong container | SMTP_HOST=mailpit (alias mạng welife_default) |
| Redis host port | REDIS_PORT=6380 (tránh conflict host redis :6379) |
Nearby PostGIS schema welife |
VPS: POSTGIS_SCHEMA=public (sync-api tự set) · local Docker có thể welife |
Yêu cầu
- Ubuntu 22.04+ / 24.04 LTS (Megahost OK) — máy hiện tại: 22.04.5
- Docker + Docker Compose plugin
- Domain + TLS (Certbot) khi lên production
- User sudo không root + SSH key (xem § VPS harden)
Tài nguyên máy chủ (Docker)
| Mức | vCPU | RAM | Disk | Ghi chú |
|---|---|---|---|---|
| Tối thiểu MVP | 2 | 4 GiB | 40 GiB SSD | api + Postgres + Redis + MinIO |
| Prod khuyến nghị | 4 | 8 GiB | 60 GiB SSD | + swap 4G · log rotate |
| Prod + OSRM VN | 4–8 | 12–16 GiB | 80+ GiB | OSRM Vietnam ~2.5 GiB RSS · data ~4 GiB |
Máy mẫu 180.93.146.58: 9 vCPU · 15 GiB · 94 GiB — đủ prod + OSRM.
# Cài Docker (nếu thiếu) + swap 4G + daemon log-rotate + sysctl
# Copy script lên VPS rồi:
bash /opt/welife/deploy/vps-docker-resources.sh
# Áp giới hạn RAM/CPU từng service
cd /opt/welife
docker compose -f docker-compose.yml -f deploy/docker-compose.resources.yml --profile full up -d
docker stats --no-stream
Không mở 5432 / 6379 / 9000 ra public. Swap chỉ dự phòng OOM — vm.swappiness=10.
VPS mới — harden + panel (WP-DEPLOY-01)
Máy mẫu: Ubuntu 24.04 · root console (noVNC Megahost).
Script repo: deploy/vps-harden-ubuntu24.sh
A. Trên console VPS (root) — bước an toàn tối thiểu
# 1) Cập nhật + công cụ
apt update && apt upgrade -y
apt install -y curl git ufw fail2ban unattended-upgrades ca-certificates
# 2) User admin (đổi tên nếu muốn)
adduser welifeadmin
usermod -aG sudo welifeadmin
mkdir -p /home/welifeadmin/.ssh && chmod 700 /home/welifeadmin/.ssh
# 3) DÁN public key máy bạn vào (bắt buộc trước khi tắt login root/password)
nano /home/welifeadmin/.ssh/authorized_keys
chmod 600 /home/welifeadmin/.ssh/authorized_keys
chown -R welifeadmin:welifeadmin /home/welifeadmin/.ssh
# 4) Firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status
# 5) fail2ban
systemctl enable --now fail2ban
Từ máy Windows (PowerShell), thử SSH:
ssh welifeadmin@180.93.146.58
Khi login bằng key OK:
# Trên VPS — siết SSH
cat >/etc/ssh/sshd_config.d/99-welife.conf <<'EOF'
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers welifeadmin
MaxAuthTries 3
EOF
systemctl reload ssh
B. Chạy script đầy đủ (Docker + harden)
Copy deploy/vps-harden-ubuntu24.sh lên VPS rồi:
export WELIFE_ADMIN_USER=welifeadmin
export WELIFE_SSH_PUBKEY='ssh-ed25519 AAAA... your-key'
# Panel (chọn 1):
# none — chỉ harden (khuyến nghị lần đầu)
# coolify — quản lý Docker/app (khớp WELIFE compose)
# aapanel — panel kiểu BT (phổ biến VN)
export WELIFE_PANEL=none
bash vps-harden-ubuntu24.sh
| Panel | Khi nào dùng | Port UI |
|---|---|---|
| Coolify | Deploy Docker compose / WELIFE stack | :8000 |
| aaPanel | Nginx/PHP quen BT Panel | :7800 / :888 |
| none | Chỉ Docker CLI + PM2 theo doc dưới | — |
Khuyến nghị WELIFE: harden xong → WELIFE_PANEL=coolify (hoặc none + Docker tay).
C. Cấm expose DB
Postgres / Redis / MinIO chỉ bind 127.0.0.1 (đã trong compose local).
UFW không mở 5432 6379 9000.
D. Secrets production
# trên máy dev
node scripts/gen-secrets.mjs
# dán vào api/.env trên VPS — xem docs/SECURITY.md + 07-ENVIRONMENT.md
Domain mẫu — minhtien.cloud (Megahost)
DNS A → 180.93.146.58 (NS DotVN: ns1.dotvndns.vn):
| Host | Vai trò | Trạng thái |
|---|---|---|
api.minhtien.cloud |
Nest API + Socket.IO → 127.0.0.1:3000 |
HTTPS OK |
ws.minhtien.cloud |
WS (cùng API) | HTTPS OK |
admin.minhtien.cloud |
Dashboard Vite | HTTPS OK |
www / docs |
Placeholder | HTTPS OK |
@ (minhtien.cloud) |
Apex | HTTPS OK (Let's Encrypt) |
Consumer Expo production:
EXPO_PUBLIC_API_URL=https://api.minhtien.cloud/api/v1
EXPO_PUBLIC_WS_URL=https://api.minhtien.cloud
EXPO_PUBLIC_ENV=production
Hoàn thiện env prod (pattern APP_DESIGN_BUILD health/deploy):
bash /root/finalize-prod-env.sh # CORS · TRUST_PROXY · recreate API · landing
bash /root/issue-apex-ssl.sh # cert @ sau khi A đúng
bash /root/health-check.sh
Scripts: finalize-prod-env.sh · issue-apex-ssl.sh · health-check.sh
One-shot (đã có aaPanel): deploy/one-shot-minhtien-cloud.sh
Từ máy Windows (sau khi tạo file gitignored .vps-pass = 1 dòng mật khẩu root):
node --use-system-ca deploy/push-and-run.mjs
Hoặc trên VPS (root):
bash one-shot-minhtien-cloud.sh
Dev infra (Postgres PostGIS + Redis + MinIO)
Ports mặc định trên máy Windows có thể lệch (5434/6380) — xem 07-ENVIRONMENT.md.
cd /opt/welife # hoặc D:\welife
cp .env.example .env
cp api/.env.example api/.env
npm run infra:up
npm run env:check
Chạy API local (ngoài Docker)
cd api
cp .env.example .env
npm install
npm run start:dev
# Swagger: http://localhost:3000/docs
Full stack Docker (API + Nginx)
docker compose --profile full up -d --build
PM2 (không dùng Docker cho Node)
sudo apt update && sudo apt install -y nodejs npm nginx
cd /opt/welife/api
npm ci && npm run build
npx pm2 start dist/main.js --name welife-api -i max
npx pm2 save
Nginx reverse proxy trỏ proxy_pass http://127.0.0.1:3000 theo deploy/nginx.conf.
Checklist production
- VPS harden (§ trên) · SSH key · UFW · fail2ban
- Đổi
JWT_*_SECRET· Redis/MinIO/Postgres password - Bật HTTPS (Let's Encrypt)
- Backup Postgres hàng ngày
- Không expose Postgres/Redis ra public
- OTP qua email (
OTP_CHANNEL=email· SMTP Mailpit hoặc mailserver) - DNS mail (MX/SPF/DKIM) nếu gửi ra Internet — MAIL-DNS.md
-
SMS_PROVIDER=http|twilio·PAYMENTS_MOCK=falsekhi live SMS/pay
Mail (WP-AUTH-05)
# Mailpit (OTP inbox nội bộ — đã dùng trên VPS)
docker ps | grep mailpit
# docker-mailserver (optional — /opt/welife-mail; port 25 thường bị aaPanel chiếm)
bash /root/mailserver-up.sh
# DNS: deploy/MAIL-DNS.md
Auth FE: login email OTP (không SĐT) trên CON / PAR / DRV / ADM.