~/saidqb cat wiki/stack/server/deploy-react.md
commit by saidqb
Deploy React/Vite (Static Build + Nginx)
Aplikasi React/Vite hasil build itu murni file statis (HTML/CSS/JS) — tidak butuh Node.js jalan di server, cukup web server yang bisa serve file statis. Struktur repo yang cocok: lihat project-app-build-dan-doc.md (folder project/build/ saja yang di-deploy).
Tiap project dapat Linux user sendiri (/home/<appuser>/app), bukan numpuk di satu folder /var/www bersama — kalau server ini dipakai buat banyak project, satu project ke-compromise tidak otomatis bisa baca file project lain.
0. Prasyarat sebelum deploy
- Node.js & npm terinstall di mesin yang dipakai untuk build (local/CI — bukan server produksi, server cuma serve file statis). Pakai nvm supaya gampang ganti versi Node per proyek:
- Install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash(lalu buka terminal baru, atausource ~/.bashrc) - Install & pakai Node LTS:
nvm install --lts && nvm use --lts - Cek hasil install:
node -v && npm -v
- Install nvm:
- Nginx sudah terinstall di server:
sudo apt install nginx -y - Akses SSH ke server (user dengan hak
sudo) untuk setup Nginx & folder deploy — disiapkan penyedia server, bukan sesuatu yang di-install - Domain sudah diarahkan (DNS A record) ke IP server — diatur di panel DNS registrar, bukan di server
rsyncterinstall di mesin build & server (biasanya sudah ada default di Linux/macOS; kalau belum:sudo apt install rsync -y)
1. Buat user khusus untuk app
sudo adduser --disabled-password --gecos "" reactapp
--disabled-password — user ini tidak bisa login pakai password (cuma diakses lewat sudo dari user SSH yang sudah masuk). --gecos "" skip pertanyaan nama/nomor telepon dsb yang tidak relevan buat service account. Home directory-nya otomatis jadi /home/reactapp.
ssh user@server 'sudo mkdir -p /home/reactapp/app && sudo chown reactapp:reactapp /home/reactapp/app'
2. Build di local/CI
npm install
npm run build # hasilnya di folder dist/ atau build/, tergantung tool
3. Salin/deploy hasil build ke server
rsync dijalankan lewat user SSH biasa (bukan reactapp — bikin reactapp bisa login SSH sendiri cuma nambah kerumitan setup key yang tidak perlu untuk static site), lalu kepemilikan file disamakan ke reactapp sesudahnya:
rsync -avz --delete dist/ user@server:/home/reactapp/app/
ssh user@server 'sudo chown -R reactapp:reactapp /home/reactapp/app'
4. Izin baca untuk Nginx
Home directory Linux defaultnya tertutup dari user lain — Nginx (jalan sebagai www-data) butuh akses baca ke folder ini biar bisa serve file statisnya:
sudo chmod o+rx /home/reactapp
sudo chmod -R o+rX /home/reactapp/app
5. Nginx — serve static + SPA fallback
server {
listen 80;
server_name app.example.com;
root /home/reactapp/app;
index index.html;
location / {
try_files $uri $uri/ /index.html; # penting untuk client-side routing (React Router)
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
try_files ... /index.html wajib ada kalau pakai client-side router (React Router, dsb) — tanpa ini, refresh di URL selain / (misal /about) akan 404 karena Nginx mencari file /about yang memang tidak ada.
sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo ufw allow 'Nginx Full' # buka port 80/443 di firewall — tanpa ini situs tidak bisa diakses dari luar meski Nginx sudah jalan benar, kalau ufw aktif (default di banyak VPS)
6. Urutan tiap kali deploy update
npm run build
rsync -avz --delete dist/ user@server:/home/reactapp/app/
ssh user@server 'sudo chown -R reactapp:reactapp /home/reactapp/app && sudo chmod -R o+rX /home/reactapp/app'
Tidak perlu restart service apa pun — file statis langsung kebaca Nginx begitu di-overwrite. Kalau pakai cache-busting hash di nama file build (default Vite), tidak perlu khawatir user dapat file lama ter-cache browser.
Opsional: HTTPS
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d app.example.com
Konfigurasi SSL-nya tidak perlu ditulis manual — plugin --nginx otomatis suntik langsung ke server block yang sudah dibuat di Langkah 5 (listen 443 ssl, path sertifikat, redirect HTTP→HTTPS), dan pasang auto-renewal sendiri (sertifikat Let's Encrypt berlaku 90 hari, diperpanjang otomatis sebelum expired). Hasilnya kira-kira begini:
server {
listen 80;
server_name app.example.com;
root /home/reactapp/app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparam.pem; # managed by Certbot
}
server {
if ($host = app.example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name app.example.com;
return 404; # managed by Certbot
}
Kalau nanti edit config ini lagi, jangan hapus baris # managed by Certbot — dan tetap sudo nginx -t && sudo systemctl reload nginx tiap habis edit.
Kalau domain dipasang di belakang Cloudflare (proxy oranye), lihat catatan cloudflare.md — ada penyesuaian mode SSL/TLS & opsi sertifikat yang perlu diperhatikan.