Tutorial Deploy & Amankan REST API FastAPI dengan Docker di VPS
Masalah Deployment Manual dan Arsitektur Container FastAPI
Deploy aplikasi Python secara langsung pada server VPS tanpa kontainerisasi kerap menimbulkan kendala operasional mendasar. Pendekatan konvensional yang mengandalkan virtualenv global atau lokal pada OS host rentan terhadap masalah environment drift, konflik dependensi sistemik (seperti versi libpq-dev atau C compiler), serta penanganan sisa alokasi memori saat proses mendadak terhenti (crash). Selain itu, pengelolaan skrip systemd manual tanpa isolasi runtime dapat mengekspos lingkungan host jika terdapat celah keamanan pada level aplikasi.
Arsitektur modern backend FastAPI menyelesaikan tantangan ini dengan memisahkan tanggung jawab sistem menjadi tiga lapisan terisolasi: kontainerisasi runtime aplikasi (Docker), manajemen alur proses internal (Uvicorn), dan penanganan enkripsi serta proteksi tepi (Nginx). Docker mengemas seluruh dependensi Python, biner sistem operasi, dan kode sumber ke dalam satu kesatuan image yang immutable. Di depan kontainer, Nginx bertindak sebagai reverse proxy layer-7 yang mengakhiri koneksi TLS/SSL, membatasi beban request, menyajikan berkas statis, dan meneruskan trafik ke kontainer melalui antarmuka loopback lokal. Uvicorn di dalam kontainer berfokus murni pada eksekusi kode I/O non-blocking (async) tanpa terbeban kalkulasi terminasi enkripsi.
Alur Lalu Lintas Data Produksi
Dalam topologi produksi yang teramankan, alur pengiriman request HTTP/HTTPS dari klien luar mengikuti alur terkontrol sebagai berikut:
- Klien / Browser: Membuka koneksi TLS/SSL ke domain publik pada port 443.
- UFW Firewall (Host VPS): Memfilter lalu lintas masuk. Hanya port 22 (SSH), 80 (HTTP), dan 443 (HTTPS) yang dibuka. Seluruh port internal (termasuk port aplikasi 8000) diblokir dari akses publik luar.
- Nginx Reverse Proxy (Host VPS): Menerima request HTTPS, memverifikasi sertifikat SSL Let's Encrypt, menerapkan header security, lalu memvalidasi header
Host. Request diteruskan ke port lokal melalui antarmuka loopback (127.0.0.1:8000). - Docker Container & Server Uvicorn: Kontainer yang terikat pada loopback menerima request dari Nginx, memproses logika aplikasi async FastAPI, lalu mengembalikan respons kembali ke Nginx untuk diteruskan ke klien.
Setup Dockerfile dan Docker Compose Optimal
Menulis Dockerfile Multi-Stage Production-Ready
Penggunaan Dockerfile dasar bertahap tunggal (single-stage build) sering kali menghasilkan ukuran image yang membengkak (mencapai 1 GB lebih) karena membawa perkakas pembuat kode (seperti gcc, g++, make) yang hanya dibutuhkan saat kompilasi paket Python tertentu. Di lingkungan produksi, biner pembuat kode tersebut tidak lagi diperlukan dan justru memperluas attack surface (area bahaya peretasan).
Pendekatan optimal adalah memanfaatkan multi-stage build. Stage pertama (builder) mengeksekusi instalasi dan kompilasi dependensi. Stage kedua (runtime) hanya menyalin hasil pustaka terkompilasi ke dalam lingkungan bersih tanpa alat build. Tambahan pula, kontainer harus dikonfigurasi agar berjalan dengan hak akses pengguna biasa (non-root) untuk mencegah eskalasi hak akses sistem (privilege escalation) pada VPS jika kontainer berhasil ditembus.
# Stage 1: Build Stage
FROM python:3.11-slim AS builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Stage 2: Runtime Stage
FROM python:3.11-slim
WORKDIR /app
# Buat non-root user demi keamanan
RUN groupadd -g 1000 appgroup && \
useradd -u 1000 -g appgroup -s /bin/sh -m appuser
# Salin pustaka terkompilasi dari stage builder
COPY --from=builder /install /usr/local
COPY . /app
# Atur kepemilikan direktori aplikasi
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4", "--proxy-headers", "--forwarded-allow-ips", "*"]Penggunaan opsi --proxy-headers dan --forwarded-allow-ips="*" pada Uvicorn sangat penting saat aplikasi berada di balik reverse proxy seperti Nginx. Opsi ini memastikan Uvicorn dapat membaca IP asli klien melalui header HTTP X-Forwarded-For dan X-Forwarded-Proto alih-alih mendeteksi alamat IP Nginx host (127.0.0.1).
Mengonfigurasi Docker Compose untuk Produksi
Docker Compose mempermudah standardisasi variabel lingkungan, isolasi jaringan, pembatasan log, serta manajemen daur hidup kontainer. Buat berkas docker-compose.yml di direktori akar proyek:
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile
container_name: fastapi_production
restart: always
ports:
- "127.0.0.1:8000:8000"
environment:
- ENVIRONMENT=production
- ALLOWED_HOSTS=api.domainanda.com
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/healthcheck"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
resources:
limits:
cpus: '1.50'
memory: 1024M
reservations:
memory: 256MPerhatikan spesifikasi ports: - "127.0.0.1:8000:8000". Penguncian ini secara eksplisit mengikat port kontainer hanya ke antarmuka loopback VPS. Tanpa penulisan IP 127.0.0.1 secara spesifik (misalnya hanya menuliskan "8000:8000"), Docker secara otomatis memanipulasi aturan iptables pada host dan membuka port tersebut ke seluruh dunia, mengabaikan aturan UFW yang telah dibuat.
Konfigurasi Nginx Reverse Proxy dan SSL Certbot
Instalasi Paket Utama dan Konfigurasi Firewall UFW
Langkah pertama di level VPS host adalah menginstal Nginx, Certbot, dan UFW (Uncomplicated Firewall). Jalankan perintah berikut pada VPS berbasis Ubuntu/Debian:
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx ufwSebelum mengaktifkan UFW, pastikan aturan akses SSH dibuka agar sesi koneksi Anda tidak terputus:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status verbosePenyusunan Server Block Nginx
Buat berkas konfigurasi baru untuk aplikasi FastAPI pada direktori Nginx host: /etc/nginx/sites-available/fastapi.
server {
listen 80;
server_name api.domainanda.com;
# Pembatasan ukuran muatan payload request (Upload max 10MB)
client_max_body_size 10M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
# Dukungan WebSocket dan Persistent Connections
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
# Meneruskan Header Identitas Klien
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;
# Pengaturan Timeout Reverse Proxy
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Blokir akses langsung ke berkas tersembunyi
location ~ /\. {
deny all;
}
}Aktifkan blok situs tersebut dengan membuat tautan simbolik (symlink) ke direktori sites-enabled, lalu uji validitas sintaksisnya:
sudo ln -s /etc/nginx/sites-available/fastapi /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxOtomatisasi Enkripsi HTTPS dengan Certbot Let's Encrypt
Gunakan Certbot untuk menginstal sertifikat TLS/SSL gratis. Certbot akan memodifikasi konfigurasi Nginx secara otomatis untuk mengalihkan seluruh lalu lintas HTTP (port 80) ke HTTPS (port 443) serta menerapkan protokol enkripsi modern.
sudo certbot --nginx -d api.domainanda.comVerifikasi bahwa timer otomatis pembaruan sertifikat (auto-renewal) berjalan dengan baik pada sistem:
sudo systemctl status certbot.timer
sudo certbot renew --dry-runHardening Keamanan Backend FastAPI
Menonaktifkan Dokumentasi Publik & Menyiapkan CORS Strict
Di lingkungan produksi, halaman dokumentasi interaktif Swagger UI (/docs) dan ReDoc (/redoc) wajib dimatikan jika API ditujukan khusus untuk konsumsi privat atau aplikasi klien internal. Membiarkan OpenAPI schema terbuka di publik memudahkan pihak luar memetakan seluruh struktur endpoint dan model data aplikasi.
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
# Deteksi lingkungan operasi
ENVIRONMENT = os.getenv("ENVIRONMENT", "development")
# Sembunyikan Swagger UI dan ReDoc saat produksi
docs_url = None if ENVIRONMENT == "production" else "/docs"
redoc_url = None if ENVIRONMENT == "production" else "/redoc"
openapi_url = None if ENVIRONMENT == "production" else "/openapi.json"
app = FastAPI(
title="Production API Service",
docs_url=docs_url,
redoc_url=redoc_url,
openapi_url=openapi_url
)
# Proteksi Host Header Injection
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["api.domainanda.com", "localhost", "127.0.0.1"]
)
# Konfigurasi Strict CORS
ALLOWED_ORIGINS = [
"https://domainanda.com",
"https://www.domainanda.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
allow_headers=["Authorization", "Content-Type", "X-Request-ID"],
)
@app.get("/healthcheck", status_code=200)
async def healthcheck():
return {"status": "healthy", "environment": ENVIRONMENT}Implementasi Rate Limiting untuk Proteksi Abuse
Tanpa pembatasan jumlah request (rate limiting), endpoint API rentan terhadap serangan Brute Force dan Denial of Service (DoS). Gunakan pustaka slowapi untuk membatasi kuota request per alamat IP klien secara terukur.
from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
limiter = Limiter(key_func=get_remote_address, default_limits=["200/minute"])
app = FastAPI(title="Production API Service")
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.post("/v1/login")
@limiter.limit("5/minute")
async def login(request: Request):
# Logika otentikasi login
return {"status": "authenticated\


