Men-deploy aplikasi FastAPI: menjalankan uvicorn multi-worker yang benar, Gunicorn sebagai process manager, multi-stage Dockerfile yang ramping, konfigurasi reverse proxy, hingga deploy ke platform cloud dan Kubernetes.

Setelah 23 episode membangun aplikasi yang lengkap — dari routing sampai AI streaming — sekarang saatnya pergi ke produksi. Episode ini menjawab pertanyaan paling penting di tahap akhir: bagaimana menjalankan FastAPI dengan benar di server, bagaimana mengemasnya ke container, dan di mana men-deploy-nya.
Mengapa episode ini penting? Banyak aplikasi yang bekerja sempurna di laptop gagal di produksi karena hal-hal sederhana: --reload yang aktif, satu worker yang kewalahan, atau image Docker yang bengkak. Episode ini membangun fondasi deployment yang benar sejak langkah pertama.
Ingat episode 3: --reload hanya untuk development. Di produksi, kita butuh banyak worker dan tanpa reload:
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4Aturan jumlah worker: 2 × jumlah core CPU + 1 (rumus umum untuk I/O-bound). Pada mesin 2-core: --workers 5. Karena workload FastAPI biasanya I/O-bound (menunggu database, HTTP, disk), worker lebih banyak membantu — namun setiap worker memakai memory, jadi sesuaikan dengan RAM yang tersedia.
Warning
Pastikan --reload tidak pernah muncul di produksi — ia memicu restart tanpa sepengetahuan dan menghabiskan resource. Dan --host 0.0.0.0 wajib agar container/server bisa diakses dari luar; 127.0.0.1 hanya untuk development lokal.
Gunicorn adalah process manager WSGI/ASGI yang lebih matang — dia mengelola worker, restart otomatis saat worker mati, dan sinyal penghentian yang rapi. Pakai Gunicorn sebagai front dan uvicorn worker sebagai engine ASGI:
pip install gunicorngunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000-k uvicorn.workers.UvicornWorker — worker ASGI dari uvicorn.-w 4 — empat worker.SIGTERM ke worker dan menunggu request selesai saat shutdown — proses restart yang lebih terkontrol.Tip
Jika aplikasi kalian banyak memakai WebSocket (episode 13), perhatikan bahwa WebSocket diikat ke satu worker — klien tidak boleh pindah worker di tengah koneksi. Di skala besar, pertimbangkan worker-manager atau session affinity di load balancer.
Docker mengemas aplikasi + environment menjadi satu artefak yang bisa dideploy di mana saja. Multi-stage memisahkan build dan runtime — hasilnya image ramping:
FROM python:3.13-slim AS builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM python:3.13-slim AS runtime
WORKDIR /app
ENV PYTHONPATH=/install
COPY --from=builder /install /install
COPY app/ ./app/
EXPOSE 8000
CMD ["gunicorn", "app.main:app", "-w", "4", \
"-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]Kenapa multi-stage? Stage builder menginstall dependensi ke /install; stage runtime menyalin hasilnya tanpa lapisan build (compiler, cache pip). Image final berisi hanya yang dibutuhkan untuk menjalankan aplikasi.
Important
Jangan copy .env ke dalam image — secret di image = secret di registry = bocor. Image hanya berisi kode dan dependensi; konfigurasi (episode 16) di-inject saat runtime lewat environment variable. SECRET_KEY dan DATABASE_URL harus hidup di platform, bukan di image.
Kecilkan build context — file yang tidak perlu ikut ke image:
.venv/
__pycache__/
*.pyc
.env
uploads/
tests/
.pytest_cache/Aplikasi sebaiknya berada di belakang reverse proxy (Nginx) — proxy menangani TLS, kompresi, dan static files:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ws/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Perhatikan location /ws/: WebSocket butuh header Upgrade khusus — tanpa itu, koneksi WebSocket gagal di balik proxy.
Warning
Setelah memakai reverse proxy, ingat dua hal dari episode 11 dan 19: TrustedHostMiddleware harus berisi server_name yang benar, dan rate limit per-IP butuh ProxyHeadersMiddleware (atau --forwarded-allow-ips) agar IP asli terdeteksi, bukan IP proxy.
Image Docker bisa dideploy ke berbagai platform dengan pola yang sama:
| Platform | Catatan |
|---|---|
| Fly.io | Containerized, mudah scale; fly deploy |
| Render | Web service dari Docker; auto-deploy dari git |
| Railway | Container langsung dari repo; env terpusat |
| Kubernetes | Skala penuh: Deployment, Service, Ingress, HPA |
Contoh minimal Kubernetes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
spec:
replicas: 3
selector:
matchLabels:
app: fastapi
template:
metadata:
labels:
app: fastapi
spec:
containers:
- name: app
image: ghcr.io/kalian/fastapi-app:latest
ports:
- containerPort: 8000
envFrom:
- secretRef:
name: app-secrets
readinessProbe:
httpGet:
path: /health
port: 8000Tiga hal yang diperhatikan di K8s: replicas untuk skala horizontal, secretRef untuk konfigurasi (episode 16), dan readinessProbe — K8s hanya mengirim trafik ke pod yang health check-nya lolos.
Tip
Endpoint /health itu bukan hiasan — ia dipakai load balancer dan orchestrator untuk memutuskan kapan pod siap menerima trafik. Tambahkan health check di aplikasi kalian sejak awal (topik observability episode 25).
| Pitfall | Solusi |
|---|---|
--reload aktif di produksi | Hapus; gunakan multi-worker |
127.0.0.1 di container | --host 0.0.0.0 |
| Secret di image | Inject via environment |
| WebSocket gagal di balik proxy | Header Upgrade di Nginx |
| Image bengkak | Multi-stage Dockerfile |
Inti yang harus dibawa pulang:
uvicorn --workers 2×core+1 atau Gunicorn + UvicornWorker.gunicorn sebagai CMD.readinessProbe + secret.Di episode 25 selanjutnya kita akan membahas performance & benchmarking — memprofil endpoint, tuning connection pooling, caching dengan Redis, dan load testing dengan k6. API kalian tidak hanya jalan — sekarang kalian buktikan seberapa cepat ia melaju!