MUHAMMAD
FARI
MADYAN
[ Press ESC or Click to Skip ]

Rebuild Python FastAPI + React E-Learning

1

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

2

Part 2: Async ORM & Type Safety: SQLAlchemy 2.0 Async, Pydantic v2 & Pencegahan Masalah N+1

3

Part 3: Engine Ujian CBT Presisi Tinggi & Telemetri Pengawasan Native WebSocket

4

Part 4: Penguatan Keamanan Enterprise: Stateless JWT, RBAC & 1-Click Impersonation

5

Part 5: Pipeline Data Excel Batch Berkecepatan Tinggi & Pengujian Asinkron Otomatis

6

Part 6: Single VPS Production Deployment: Docker Compose, Nginx SSL & Monitoring Pemulihan Otomatis

Artikel ini tersedia dalam Bahasa Inggris

🇬🇧 Read in English
🇮🇩 Bahasa Indonesia📚 Rebuild Python FastAPI + React E-Learning

Python FastAPI E-Learning Part 6: Single VPS Production Deployment – Docker Compose, Nginx SSL & Monitoring Pemulihan Otomatis

Rilis aplikasi performa tinggi Python FastAPI dan React 19 ke server Linux VPS tunggal: orkestrasi multi-kontainer Docker Compose, Host Nginx reverse proxy dengan SSL Let's Encrypt, tuning WebSocket upgrade, serta skrip monitoring auto-recovery.

Muhammad Fari MadyanPenulis

8 menit baca

·

9 Oktober 2026


Cetak Biru Deployment Server Produksi

Membawa aplikasi asinkron modern berbasis FastAPI + React dari laptop lokal ke server produksi enterprise menuntut perhatian infrastruktur yang tidak dapat diselesaikan hanya dengan optimasi kode semata:

  1. Timeout Proxy WebSocket: Nginx secara default memutus koneksi TCP idle setelah 60 detik. Di tengah pelaksanaan ujian sekolah, pemutusan sepihak ini akan mematikan layar telemetri pengawas guru secara tiba-tiba.
  2. Isolasi Sumber Daya pada Hardware Ekonomis: Institusi pendidikan umumnya menggunakan Virtual Private Server (VPS) dengan spesifikasi terjangkau (misal: 2 vCPU / 4GB RAM atau 4 vCPU / 8GB RAM). Seluruh stack (FastAPI, React, PostgreSQL 16, dan Redis 7) harus berjalan harmonis tanpa memicu kepanikan memori Out-Of-Memory (OOM) killer Linux.
  3. Pemulihan Otomatis (Self-Healing) & Peringatan Dini: Jika lonjakan konkurensi memicu crash pada kontainer backend pada pukul 08:00 WIB, sistem harus mampu melakukan restart otomatis dalam hitungan detik sembari mengirimkan alert webhook ke tim teknis.

Pada proyek mfarim/fastapi-elearning-react, kami merancang arsitektur produksi tangguh pada server VPS tunggal (single VPS).

┌────────────────────────────────────────────────────────────────────────────────────────┐ │ INFRASTRUKTUR PRODUKSI SERVER VPS TUNGGAL │ └────────────────────────────────────────────────────────────────────────────────────────┘ HTTPS (:443) / WSS (:443) │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────┐ │ SISTEM HOST: NGINX REVERSE PROXY & CERTBOT SSL │ │ • Sertifikat TLS 1.3 Otomatis dari Let's Encrypt │ │ • Tunneling WebSocket Upgrade (proxy_read_timeout 86400s) │ │ • Kompresi Aset Statis Gzip / Brotli │ │ • Header Keamanan Ketat: HSTS, XSS-Protection, X-Frame-Options │ └─────────────────────────┬───────────────────────────────┬────────────────────────┘ │ │ Proxy Pass http:// │ :3001 │ Proxy Pass http:// :8000 (Aplikasi Frontend SPA)│ │ (REST API & WebSockets) ▼ ▼ ┌──────────────────────────────────────────────────────────────────────────────────┐ │ JARINGAN ORKESTRASI DOCKER COMPOSE (bridge: elearning-network) │ │ │ │ ┌───────────────────────┐ ┌───────────────────────────────┐ │ │ │ frontend container │ │ backend container │ │ │ │ • Nginx Alpine │ │ • Python 3.12-slim │ │ │ │ • React 19 Build │ │ • Uvicorn ASGI Workers │ │ │ └───────────────────────┘ └───────────────┬───────────────┘ │ │ │ │ │ ▼ │ │ ┌───────────────────────┐ ┌───────────────────────────────┐ │ │ │ postgres container │ │ redis container │ │ │ │ • PostgreSQL 16 │ │ • Redis 7 Alpine │ │ │ │ • Healthcheck pg_isready │ • Healthcheck redis-cli ping│ │ │ └───────────────────────┘ └───────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────┘ ▲ │ Pengecekan berkala tiap 5 menit via cron ┌─────────────────────────┴────────────────────────────────────────────────────────┐ │ SKRIP WATCHDOG HOST CRONJOB (scripts/health-monitor.sh) │ │ • Menguji endpoint GET http://localhost:8000/health │ │ • Jika gagal: Mengeksekusi 'docker compose restart backend' │ │ • Mengirim notifikasi darurat via Webhook Discord / Telegram / Slack │ └──────────────────────────────────────────────────────────────────────────────────┘

1. Orkestrasi Multi-Kontainer dengan Docker Compose

Pada berkas docker-compose.yml, seluruh service didefinisikan lengkap beserta healthcheck, kebijakan restart, dan volume penyimpanan data persisten:

YAML

# docker-compose.yml
services:
  postgres:
    image: postgres:16-alpine
    container_name: fastapi-elearning-postgres
    restart: always
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-elearning}
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
    ports:
      - "127.0.0.1:5434:5432"  # Ikat hanya ke localhost untuk keamanan
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - elearning-network

  redis:
    image: redis:7-alpine
    container_name: fastapi-elearning-redis
    restart: always
    ports:
      - "127.0.0.1:6380:6379"  # Ikat hanya ke localhost
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - elearning-network

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: fastapi-elearning-backend
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-elearning}
      REDIS_URL: redis://redis:6379/0
      SECRET_KEY: ${SECRET_KEY}
      ACCESS_TOKEN_EXPIRE_MINUTES: 120
    ports:
      - "127.0.0.1:8000:8000"
    volumes:
      - ./backend/storage:/app/storage
    networks:
      - elearning-network

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: fastapi-elearning-frontend
    restart: always
    ports:
      - "127.0.0.1:3001:80"
    networks:
      - elearning-network

volumes:
  postgres_data:
  redis_data:

networks:
  elearning-network:
    driver: bridge

Praktik Keamanan Krusial:

  1. Binding Port Khusus Localhost (127.0.0.1:5434): Port database dan Redis hanya mendengarkan request dari dalam mesin VPS, mencegah pemindaian port terbuka secara langsung dari internet publik.
  2. depends_on dengan service_healthy: Kontainer backend menunda proses booting sampai PostgreSQL berhasil lolos pengecekan pg_isready, mengeliminasi error connection refused pada saat server restart.

2. Dockerfile Produksi: Python 3.12 Ramping & Efisien

Di backend/Dockerfile, kami membangun image multi-layer berbasis python:3.12-slim:

DOCKERFILE

# backend/Dockerfile
FROM python:3.12-slim

WORKDIR /app

# Pasang pustaka sistem yang dibutuhkan untuk kompilasi biner asyncpg & healthcheck
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libpq-dev \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Cache dependensi Python
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt

# Salin seluruh kode aplikasi
COPY . .

# Buat direktori penyimpanan berkas jika belum ada
RUN mkdir -p /app/storage/uploads

EXPOSE 8000

# Jalankan Uvicorn dengan konfigurasi worker proses teroptimasi
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]

3. Host Nginx Reverse Proxy dengan Konfigurasi WSS & SSL

Pada sistem operasi host VPS, Nginx bertindak sebagai gerbang terdepan. Konfigurasi di bawah ini menyediakan terminasi SSL, header keamanan, dan yang paling utama: stabilitas koneksi persisten WebSocket:

NGINX

# /etc/nginx/sites-available/elearning.conf

# 1. Redirection Otomatis HTTP ke HTTPS
server {
    listen 80;
    server_name elearning.sekolah.id;
    return 301 https://$host$request_uri;
}

# 2. Server HTTPS Produksi
server {
    listen 443 ssl http2;
    server_name elearning.sekolah.id;

    # Sertifikat SSL dari Let's Encrypt via Certbot
    ssl_certificate /etc/letsencrypt/live/elearning.sekolah.id/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/elearning.sekolah.id/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Batas ukuran upload untuk berkas Excel dan Tugas Siswa
    client_max_body_size 30M;

    # Header Keamanan Tingkat Lanjut
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # --- Proxy REST API ---
    location /api/ {
        proxy_pass http://127.0.0.1:8000/api/;
        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;
    }

    # --- Dokumentasi Interaktif API ---
    location /docs {
        proxy_pass http://127.0.0.1:8000/docs;
        proxy_set_header Host $host;
    }
    location /openapi.json {
        proxy_pass http://127.0.0.1:8000/openapi.json;
        proxy_set_header Host $host;
    }

    # --- Tunneling WebSocket Telemetri Pengawas ---
    location /ws/ {
        proxy_pass http://127.0.0.1:8000/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;
        
        # KRUSIAL: Mencegah Nginx memutuskan koneksi WebSocket pengawas setelah 60 detik idle
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }

    # --- Frontend SPA (React 19) ---
    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Konfigurasi Krusial: proxy_read_timeout 86400s

Secara default, Nginx memiliki batas waktu baca 60 detik (proxy_read_timeout 60s). Jika seorang siswa sedang berkonsentrasi membaca soal esai yang rumit dan tidak ada event telemetri yang dikirimkan selama 60 detik, Nginx akan memutus koneksi socket secara sepihak.

Penyetelan proxy_read_timeout 86400s (24 jam) menjaga koneksi telemetri tetap hidup sepanjang hari ujian sekolah.


4. Skrip Monitoring Kesehatan & Pemulihan Otomatis (Auto-Recovery)

Bahkan aplikasi yang dirancang paling teliti sekalipun dapat mengalami kegagalan di luar kendali. Di scripts/health-monitor.sh, kami membangun skrip pengawas mandiri:

BASH

#!/usr/bin/env bash
# ==============================================================================
# Skrip Watchdog Monitoring Kesehatan & Auto-Recovery Produksi
# scripts/health-monitor.sh
# ==============================================================================
set -euo pipefail

TARGET_URL="${HEALTH_CHECK_URL:-http://localhost:8000/health}"
WEBHOOK_URL="${ALERT_WEBHOOK_URL:-https://discord.com/api/webhooks/your_webhook_here}"
TIMEOUT_SECONDS=5
PROJECT_DIR="/var/www/python-fastapi-react-elearning"
LOG_FILE="/var/log/elearning-health.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

send_alert() {
    local message="$1"
    if [ -n "$WEBHOOK_URL" ]; then
        curl -s -X POST -H "Content-Type: application/json" \
            -d "{\"content\": \"⚠️ **ALERT: Insiden Sistem E-Learning**\n$message\"}" \
            "$WEBHOOK_URL" > /dev/null 2>&1 || true
    fi
}

# 1. Pindai endpoint healthcheck backend
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time "$TIMEOUT_SECONDS" "$TARGET_URL" || echo "000")

if [ "$HTTP_STATUS" -eq 200 ]; then
    log "KONDISI NORMAL: API mengembalikan HTTP 200"
    exit 0
else
    log "KEGAGALAN SISTEM: API mengembalikan kode HTTP $HTTP_STATUS! Memulai prosedur auto-recovery..."
    send_alert "Backend FastAPI tidak merespons (HTTP $HTTP_STATUS) di server $(hostname). Melakukan restart kontainer otomatis..."

    # 2. Eksekusi restart kontainer otomatis
    cd "$PROJECT_DIR"
    docker compose restart backend

    # 3. Tunggu dan verifikasi ulang status sistem
    sleep 10
    RETRY_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time "$TIMEOUT_SECONDS" "$TARGET_URL" || echo "000")

    if [ "$RETRY_STATUS" -eq 200 ]; then
        log "PEMULIHAN BERHASIL: Backend kembali normal (HTTP 200)"
        send_alert "✅ **PEMULIHAN BERHASIL**: Backend FastAPI berhasil pulih dan aktif kembali di server $(hostname)."
    else
        log "KRITIKAL: Auto-recovery gagal! Membutuhkan intervensi manual tim engineer."
        send_alert "🚨 **PERINGATAN KRITIKAL**: Auto-recovery gagal! Backend tetap mati (HTTP $RETRY_STATUS)."
    fi
fi

Penjadwalan Berkala via Cron Host

Daftarkan skrip ke crontab server VPS untuk berjalan setiap 5 menit:

BASH

# Tambahkan ke crontab host (crontab -e):
*/5 * * * * /var/www/python-fastapi-react-elearning/scripts/health-monitor.sh >> /var/log/elearning-cron.log 2>&1

Jika terjadi kendala pada akhir pekan atau malam hari, pengawas akan merestart kontainer dalam 5 menit, memulihkan layanan, dan mengirimkan ringkasan insiden ke kanal Discord/Slack tim engineering.


Rekapitulasi Lengkap 6 Bab Series Rebuild

Melalui rangkaian 6 bab komprehensif ini, kita telah mentransformasikan platform ujian monolitik Laravel menjadi arsitektur modern berkecepatan tinggi berbasis Python FastAPI:

PartTopik PembahasanIntisari Rekayasa Arsitektur
Part 1Arsitektur & Migrasi MonolitPerbandingan runtime sinkron PHP-FPM vs asinkron ASGI, komparasi 3 stack (Laravel vs Java vs Python).
Part 2Async ORM & Type SafetySQLAlchemy 2.0 Async, mitigasi query N+1 via selectinload, serta keamanan expire_on_commit=False.
Part 3Engine Ujian CBT Presisi TinggiSisa waktu otoritatif server, ambang batas 5x pelanggaran curang, dan native ASGI WebSocket dengan Redis.
Part 4Penguatan Keamanan EnterpriseStateless JWT, RBAC deklaratif, dan 1-Click admin impersonation dengan pemulihan sesi instan.
Part 5Pipeline Data & Jaminan KualitasStreaming openpyxl hemat memori (<15MB), transaksi batch atomik, serta pengujian otomatis Pytest-Asyncio.
Part 6Deployment VPS & Monitoring ProduksiDocker Compose multi-kontainer, Nginx SSL reverse proxy, tuning WebSocket, serta skrip monitoring auto-recovery.

Seluruh kode sumber, konfigurasi Docker, dan pengujian otomatis tersedia secara open-source di GitHub: mfarim/fastapi-elearning-react.

Lanjut Membaca

Previous article thumbnail

← Artikel Sebelumnya

Python FastAPI E-Learning Part 5: High-Throughput Excel Pipelines & Async Automated Testing with Pytest

Artikel Selanjutnya →

Rebuilding Laravel E-Learning to Ruby on Rails & React: Convention over Configuration, Multithreaded Puma & Cloud-Ready Migration

Next article thumbnail