Logo

~/saidqb cat wiki/stack/server/deploy-react.md

MDdeploy-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, atau source ~/.bashrc)
    • Install & pakai Node LTS: nvm install --lts && nvm use --lts
    • Cek hasil install: node -v && npm -v
  • 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
  • rsync terinstall 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.