MUHAMMAD
FARI
MADYAN
[ Press ESC or Click to Skip ]

Rebuild Java Spring Boot + React E-Learning

1

Part 1: Pengenalan & Arsitektur: Dari Monolit ke Arsitektur Enterprise Terpisah

2

Part 2: Penguatan Keamanan & Kesiapan Produksi: Path Traversal, Rotasi Refresh Token & Ketahanan Docker

3

Part 3: Panduan Deployment Single VPS: Docker Compose, Host Nginx Reverse Proxy & SSL Let's Encrypt

Artikel ini tersedia dalam Bahasa Inggris

🇬🇧 Read in English
🇮🇩 Bahasa Indonesia📚 Rebuild Java Spring Boot + React E-Learning

Java Spring Boot + React E-Learning: Panduan Deployment Single VPS dengan Docker Compose, Host Nginx Reverse Proxy & SSL Let's Encrypt

Panduan komprehensif langkah demi langkah deployment aplikasi Java 21 Spring Boot 3 & React 19 E-Learning pada satu server VPS Ubuntu: topologi Docker Compose produksi, SSL termination Nginx Host dengan Certbot, alokasi memori Swap, proxy WebSocket STOMP, backup database otomatis, dan script redeployment zero-downtime.

Muhammad Fari MadyanPenulis

11 menit baca

·

13 Oktober 2026


Pendahuluan: Dari Kontainer Lokal Menuju Lingkungan Produksi Nyata

Pada Part 2 dari series ini, kita telah membentengi platform pendamping open-source kita—mfarim/spring-elearning-react—dari celah keamanan path traversal, memasang rotasi token refresh otomatis, dan menambahkan probe kesehatan Spring Boot Actuator.

Kini saatnya melangkah ke tahap operasional sesungguhnya: menerbitkan sistem ke server publik nyata.

Bagi institusi sekolah mandiri, universitas, atau startup edukasi, menyewa klaster Kubernetes atau managed cloud multi-instance (seharga $150–$300/bulan) sering kali tidak realistis dan membebani anggaran. Sebaliknya, satu server Single Virtual Private Server (VPS) seharga $6–$15/bulan sanggup melayani ribuan peserta ujian secara stabil jika dirancang dengan isolasi kontainer, konfigurasi reverse proxy yang tepat, dan alokasi memori swap yang memadai.

Panduan ini mendokumentasikan blueprint deployment langkah demi langkah yang telah teruji di lapangan untuk menerbitkan Spring E-Learning & CBT pada subdomain produksi: 👉 https://spring-elearning.mfarim.com

┌─────────────────────────────────────────────────────────────────────────────┐ │ RINGKASAN DEPLOYMENT LINGKUNGAN PRODUKSI │ ├──────────────────────────────────────┬──────────────────────────────────────┤ │ Server Target │ Single VPS Ubuntu (22.04 / 24.04 LTS)│ │ Subdomain Produksi │ https://spring-elearning.mfarim.com │ │ Layanan Backend │ Java 21 LTS + Spring Boot 3.4.3 API │ │ Layanan Frontend │ React 19 SPA + Vite 6 + Tailwind v4 │ │ Topologi Kontainer │ Docker Compose Produksi (4 Kontainer)│ │ Reverse Proxy & SSL │ Host Nginx + Let's Encrypt (Certbot) │ │ Pemetaan Port Internal │ 127.0.0.1:8088 -> Kontainer Port 80 │ │ Isolasi Database & Cache │ PostgreSQL 16 & Redis 7 (Internal) │ │ Persistensi Data │ Named Volume Docker (DB & Storage) │ │ Pemeliharaan & Backup │ Cronjob Harian Otomatis (pg_dump) │ └──────────────────────────────────────┴──────────────────────────────────────┘

1. Arsitektur Deployment Single VPS

Pada single VPS, Nginx yang terpasang di Host VPS bertugas mengelola port 80 (HTTP) dan 443 (HTTPS) serta menghentikan SSL (SSL Termination). Nginx Host kemudian meneruskan traffic secara aman ke kontainer Docker di internal localhost pada port loopback khusus (127.0.0.1:8088):

┌─────────────────────────────────────────────────────────────────────────────┐ │ SISTEM OPERASI HOST (VPS) │ │ │ │ Pengguna Internet / Browser │ │ │ │ │ HTTPS :443 (spring-elearning.mfarim.com) │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ HOST NGINX REVERSE PROXY │ │ │ │ • SSL Termination via Let's Encrypt (Certbot) │ │ │ │ • Header WebSocket Upgrade (HTTP/1.1) │ │ │ │ • Buffer Ukuran Berkas Upload (client_max_body_size 50M) │ │ │ └──────────────────────────────────┬──────────────────────────────────┘ │ │ │ │ │ Proxy Pass ke │ │ 127.0.0.1:8088 │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ DOCKER BRIDGE NETWORK │ │ │ │ │ │ │ │ ┌──────────────────────┐ ┌──────────────────────────┐ │ │ │ │ │ elearning-frontend │ │ elearning-backend │ │ │ │ │ │ (Nginx + React 19) │──/api/*──▶│ (Spring Boot 21) │ │ │ │ │ │ Mendengarkan :8088 │──/ws/*───▶│ Port 8080 (Internal) │ │ │ │ │ └──────────────────────┘ └────────────┬─────────────┘ │ │ │ │ │ │ │ │ │ ┌────────────────────────────┼───────────┐ │ │ │ │ ▼ ▼ ▼ │ │ │ │ ┌───────────────────┐ ┌───────────────┐ ┌───────┐ │ │ │ │ │ elearning-postgres│ │elearning-redis│ │Volume │ │ │ │ │ │ PostgreSQL 16 │ │ Redis 7 │ │Storage│ │ │ │ │ │ (Port 5432 Int.) │ │(Port 6379 Int)│ │/upload│ │ │ │ │ └───────────────────┘ └───────────────┘ └───────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘

Keuntungan Pola Arsitektur Ini:

  1. Koeksistensi dengan Aplikasi Lain: Port 80 dan 443 tetap dipegang oleh Host Nginx, memungkinkan server yang sama menjalankan portofolio/blog utama (mfarim.com) tanpa bentrok port.
  2. Keamanan Database Terisolasi: PostgreSQL dan Redis tidak dibuka ke internet publik (0.0.0.0), terlindung dari serangan brute force credential.
  3. Persistensi Data Mutlak: Seluruh materi tugas, PDF silabus, dan data tabel tersimpan di named volume Docker (postgres_data, redis_data, backend_storage).

2. Spesifikasi Server & Persiapan Sistem

Spesifikasi Minimum yang Direkomendasikan:

  • OS: Ubuntu 22.04 LTS atau 24.04 LTS (64-bit)
  • CPU: Minimal 2 vCPU
  • RAM: Minimal 2 GB (Wajib dialokasikan Swap Memory 2–4 GB agar kompilasi Java Maven dan Vite tidak terkena terminasi Linux OOM Killer). Rekomendasi ideal: 4 GB RAM.
  • Storage: Minimal 25 GB SSD/NVMe

Langkah 1: Pembaruan Sistem & Alokasi Swap Memory

Masuk ke VPS via SSH:

BASH

ssh root@<IP_PUBLIC_VPS>

Perbarui paket repositori Ubuntu:

BASH

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git ufw htop ca-certificates gnupg lsb-release

Alokasi Swap Memory (Wajib untuk RAM 2 GB):

Kompilasi TypeScript di Vite dan startup JVM Java 21 membutuhkan lonjakan memori sementara. Buat Swap 4 GB:

BASH

# Periksa apakah swap sudah aktif
sudo swapon --show

# Buat file swap 4 GB
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# Jadikan swap permanen setelah restart server
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# Optimasi nilai swappiness
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

Konfigurasi Firewall (UFW):

Amankan VPS dengan membuka hanya port penting:

BASH

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp      # SSH
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS
sudo ufw enable

3. Konfigurasi DNS Subdomain

Sebelum memasang sertifikat SSL, arahkan subdomain ke IP Address VPS pada panel DNS penyedia domain Anda:

Tipe RecordNama Host / SubdomainTarget / Points ToTTLCatatan
Aspring-elearning<IP_PUBLIC_VPS>300 / AutoJika memakai Cloudflare, matikan Proxy (DNS Only / Grey Cloud) saat pertama kali validasi SSL.

Verifikasi propagasi DNS:

BASH

dig +short spring-elearning.mfarim.com
# Harus memunculkan IP Public VPS Anda

4. Instalasi Docker, Nginx Host & Certbot

Install Docker Engine & Docker Compose via Repository Resmi:

BASH

# Hapus paket lama jika ada
sudo apt remove -y docker docker-engine docker.io containerd runc

# Pasang keyring resmi Docker
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Tambahkan repository Docker
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker CE dan Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Aktifkan service Docker
sudo systemctl enable docker
sudo systemctl start docker

# Verifikasi
docker compose version

Install Nginx Host & Certbot:

BASH

sudo apt install -y nginx certbot python3-certbot-nginx
sudo systemctl enable nginx
sudo systemctl start nginx

5. Setup Repositori & Environment Produksi (.env)

Kloning repositori ke direktori standar server /var/www/spring-elearning:

BASH

sudo mkdir -p /var/www/spring-elearning
sudo chown -R $USER:$USER /var/www/spring-elearning

git clone https://github.com/mfarim/spring-elearning-react.git /var/www/spring-elearning
cd /var/www/spring-elearning

Buat File .env Produksi:

BASH

cp .env.example .env
nano .env

Sesuaikan variabel dengan kredensial kuat:

INI

# ==============================================================================
# Variabel Environment Produksi
# ==============================================================================

# --- Kredensial Database ---
POSTGRES_DB=elearning
POSTGRES_USER=elearning_prod_user
# Buat password acak yang kuat (misal via: openssl rand -hex 16)
POSTGRES_PASSWORD=Secur3_P0stgr3s_Pr0d_K3y_2026!

# --- JWT Signing Key (Wajib string Base64 256-bit!) ---
# Generate via: openssl rand -base64 32
JWT_SECRET=WjgrSmZkN1Fsa1ZReTh4V2tQd3JzZXQ4N2FscTNyd2Vxdzh5aGphaXN1Zz0=

# --- Path Penyimpanan di Dalam Kontainer ---
STORAGE_UPLOAD_DIR=/app/storage/uploads

# --- Port Forwarding Internal ke Host Nginx ---
FRONTEND_PORT=8088
BACKEND_PORT=8080

[!IMPORTANT] Cara Menghasilkan Kunci JWT Secret yang Sah: Jalankan openssl rand -base64 32 di terminal dan salin string hasilnya ke JWT_SECRET= di file .env.


6. Menjalankan Docker Compose Produksi

Gunakan konfigurasi docker-compose.prod.yml yang mengarahkan frontend ke loopback port 127.0.0.1:8088:

BASH

docker compose -f docker-compose.prod.yml up -d --build

Memeriksa Status Kontainer:

BASH

docker compose -f docker-compose.prod.yml ps

Seluruh kontainer harus berstatus healthy / up:

  • elearning-postgres (healthy)
  • elearning-redis (healthy)
  • elearning-backend (healthy)
  • elearning-frontend (up, terikat ke 127.0.0.1:8088->80/tcp)

Memeriksa Log Startup Backend:

BASH

docker compose -f docker-compose.prod.yml logs -f backend

Pastikan log menampilkan:

TEXT

Tomcat started on port 8080 (http) with context path '/'
Started BackendApplication in 8.412 seconds

Verifikasi Health Check Internal:

BASH

curl -s http://127.0.0.1:8088/actuator/health
# Respons: {"status":"UP"}

7. Konfigurasi Virtual Host Nginx Host

Sekarang atur Nginx di Host VPS agar meneruskan traffic dari subdomain spring-elearning.mfarim.com ke port internal 127.0.0.1:8088 dengan dukungan unggah file 50MB dan WebSocket persistent.

Buat File Konfigurasi Nginx:

BASH

sudo nano /etc/nginx/sites-available/spring-elearning.mfarim.com

Isi konfigurasi berikut:

NGINX

server {
    server_name spring-elearning.mfarim.com;

    # Izinkan unggah modul pembelajaran hingga 50MB
    client_max_body_size 50M;

    access_log /var/log/nginx/spring-elearning_access.log;
    error_log /var/log/nginx/spring-elearning_error.log;

    # Frontend SPA & REST API
    location / {
        proxy_pass http://127.0.0.1:8088;
        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;
    }

    # WebSocket STOMP untuk Telemetri Ujian CBT Real-Time
    location /ws {
        proxy_pass http://127.0.0.1:8088/ws;
        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_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }

    listen 80;
}

Aktifkan Konfigurasi & Uji Sintaks:

BASH

sudo ln -s /etc/nginx/sites-available/spring-elearning.mfarim.com /etc/nginx/sites-enabled/
sudo nginx -t

# Jika sintaks valid, reload Nginx:
sudo systemctl reload nginx

8. Pemasangan Sertifikat SSL (HTTPS) dengan Certbot

Pasang sertifikat TLS/SSL gratis dengan auto-redirect HTTP ke HTTPS:

BASH

sudo certbot --nginx -d spring-elearning.mfarim.com

Certbot akan menanyakan alamat email dan persetujuan Terms of Service. Setelah itu, Certbot akan memvalidasi domain via ACME challenge dan memperbarui konfigurasi Nginx dengan port 443 SSL.

Uji Perpanjangan Otomatis SSL:

BASH

sudo certbot renew --dry-run

Output: Congratulations, all simulated renewals succeeded.


9. Checklist Verifikasi Fitur Produksi

Buka browser Anda dan akses: 👉 https://spring-elearning.mfarim.com

Jalankan pengujian 5 langkah berikut:

Parameter PengujianAksiHasil yang Diharapkan
1. Gembok SSLPeriksa bilah alamat browserSertifikat TLS valid terenkripsi oleh Let's Encrypt
2. Login Super AdminMasuk dengan [email protected] / passwordMasuk ke dashboard admin dengan ringkasan statistik
3. Refresh TokenPeriksa Network Tab saat sesi aktifBackground endpoint /api/v1/auth/refresh menjaga sesi
4. Unggah FileUnggah modul materi berukuran 15MBBerkas terunggah sukses tanpa error HTTP 413
5. Live WebSocketBuka menu Monitoring Ujian CBT (/exams/:id/monitor)Konsol browser menampilkan status STOMP connected

10. Script Redeployment Otomatis (deploy.sh)

Untuk mempermudah pembaruan kode di kemudian hari, buat script deploy di /var/www/spring-elearning/deploy.sh:

BASH

cat << 'EOF' > /var/www/spring-elearning/deploy.sh
#!/bin/bash
set -e

echo "🚀 [1/4] Menarik perubahan kode terbaru dari GitHub..."
cd /var/www/spring-elearning
git pull origin main

echo "📦 [2/4] Melakukan build dan restart kontainer..."
docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up -d --remove-orphans

echo "🧹 [3/4] Membersihkan Docker image lama yang tidak terpakai..."
docker image prune -f

echo "✅ [4/4] Memeriksa status kesehatan kontainer..."
sleep 10
docker compose -f docker-compose.prod.yml ps

echo "🎉 Deployment berhasil dan aplikasi berjalan lancar!"
EOF

chmod +x /var/www/spring-elearning/deploy.sh

Kapan saja Anda ingin deploy update, cukup jalankan:

BASH

./deploy.sh

11. Backup Database Harian Otomatis

Cegah kehilangan nilai ujian dan bank soal dengan cronjob backup database berkala.

Buat Script Backup:

BASH

sudo mkdir -p /var/backups/elearning
cat << 'EOF' > /var/backups/elearning/backup-db.sh
#!/bin/bash
BACKUP_DIR="/var/backups/elearning"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
FILENAME="$BACKUP_DIR/elearning_db_$TIMESTAMP.sql.gz"

# Dump database PostgreSQL langsung dari dalam kontainer dan kompres
docker exec elearning-postgres pg_dump -U postgres elearning | gzip > "$FILENAME"

# Hapus backup yang lebih lama dari 14 hari
find "$BACKUP_DIR" -type f -name "*.sql.gz" -mtime +14 -delete

echo "Backup database selesai: $FILENAME"
EOF

chmod +x /var/backups/elearning/backup-db.sh

Pasang Cronjob (Jalan Otomatis Pukul 02:00 Pagi Setiap Hari):

BASH

sudo crontab -e

Tambahkan:

CRON

0 2 * * * /var/backups/elearning/backup-db.sh >> /var/log/elearning_backup.log 2>&1

12. Troubleshooting Masalah Populer di Lingkungan Produksi

1. Bind for 0.0.0.0:80 failed: port is already allocated

  • Penyebab: Nginx Host sudah menggunakan port 80.
  • Solusi: Di docker-compose.prod.yml, kontainer frontend dipetakan ke 127.0.0.1:8088:80, bukan port 80 host. Pastikan Anda menjalankan compose dengan -f docker-compose.prod.yml.

2. 413 Request Entity Too Large saat Mengunggah Materi

  • Penyebab: Batas body default Nginx adalah 1MB.
  • Solusi: Pastikan client_max_body_size 50M; terpasang di kedua tempat: Nginx Host (/etc/nginx/sites-available/...) dan Nginx Kontainer (frontend/nginx.conf), lalu reload Nginx host: sudo systemctl reload nginx.

3. Kontainer Backend Mati dengan Status Exit 137 (OOM Killed)

  • Penyebab: Memori RAM habis sehingga kernel Linux mematikan proses JVM.
  • Solusi:
    1. Pastikan Swap Memory minimal 2 GB–4 GB aktif (swapon --show).
    2. Batasi konsumsi heap JVM di backend/Dockerfile dengan opsi -Xms256m -Xmx512m pada ENTRYPOINT.

4. WebSocket Terputus / Gagal Handshake pada Ujian CBT

  • Penyebab: Nginx Host tidak mem-forward header Upgrade dan Connection.
  • Solusi: Pastikan blok location /ws pada konfigurasi Nginx memuat proxy_set_header Upgrade $http_upgrade; dan proxy_read_timeout 86400s;.

5. Memuat Data Demo / Uji Coba Soal & Akun

Untuk memuat akun guru, siswa, dan bank soal contoh dari seed_demo_data.sql:

BASH

docker exec -i elearning-postgres psql -U postgres -d elearning < /var/www/spring-elearning/backend/src/main/resources/db/migration/seed_demo_data.sql

Kesimpulan & Langkah Selanjutnya

Menerbitkan aplikasi skala enterprise tidak harus mahal. Dengan memadukan isolasi kontainer Docker Compose, reverse proxy Nginx Host dengan SSL Certbot, tuning memori Swap, dan backup otomatis, aplikasi Java 21 Spring Boot 3 & React 19 E-Learning dapat berjalan kokoh, aman, dan hemat biaya pada satu VPS.

Pada Part 4, kita akan membedah Desain Model Domain & Skema Flyway PostgreSQL, menelusuri bagaimana relasi database dirancang untuk keandalan data jangka panjang.

Lanjut Membaca

Previous article thumbnail

← Artikel Sebelumnya

Java Spring Boot + React E-Learning: Production Readiness, Security Hardening & Zero-Downtime Resilience (PR #1 Breakdown)

Artikel Selanjutnya →

Rebuilding Laravel E-Learning to Python FastAPI & React: High-Concurrency Async Architecture & Cloud-Ready Migration

Next article thumbnail