~/saidqb cat wiki/stack/server/deploy-fastapi.md
commit by saidqb
Deploy FastAPI (Uvicorn/Gunicorn + Nginx + systemd)
Urutan setup service Python di server, dari nol sampai bisa diakses lewat domain. 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
- Akses SSH ke server (user dengan hak
sudo) — 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
- Python 3 +
venv+pip:sudo apt install python3 python3-venv python3-pip -y(cek:python3 --version) - Nginx sudah terinstall:
sudo apt install nginx -y - Git terinstall di server:
sudo apt install git -y(dipakai untukgit pulltiap deploy) - Text editor di server buat edit systemd unit/config, minimal
nanoatauvim— server diakses lewat SSH, tidak ada GUI editor:sudo apt install vim -y
1. Buat user khusus untuk app
sudo adduser --disabled-password --gecos "" fastapiapp
--disabled-password — user ini tidak bisa login pakai password (cuma diakses lewat sudo -iu fastapiapp 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/fastapiapp.
2. Ambil kode & siapkan environment (jalan sebagai fastapiapp)
sudo -iu fastapiapp
git clone <url-repo-git> app
cd app
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install gunicorn uvicorn
exit # kembali ke user SSH biasa
sudo -iu fastapiapp masuk sebagai user itu dengan $HOME=/home/fastapiapp, jadi git clone ... app otomatis jadi /home/fastapiapp/app — seluruh isinya (termasuk venv/) otomatis kepemilikan fastapiapp.
3. Buat systemd service
/etc/systemd/system/app.service
[Unit]
Description=FastAPI app
After=network.target
[Service]
User=fastapiapp
Group=fastapiapp
WorkingDirectory=/home/fastapiapp/app
ExecStart=/home/fastapiapp/app/venv/bin/gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 -b 127.0.0.1:8000
Restart=always
[Install]
WantedBy=multi-user.target
Karena service jalan sebagai fastapiapp — pemilik asli semua filenya — tidak perlu chown/chmod tambahan seperti kalau service jalan sebagai www-data yang beda user dari yang nge-clone kodenya.
sudo systemctl daemon-reload
sudo systemctl enable app
sudo systemctl start app
4. Reverse proxy Nginx
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
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). Port 8000 tidak perlu dibuka karena cuma diakses lewat proxy_pass internal.
5. Urutan tiap kali deploy update
sudo -iu fastapiapp
cd app
git pull origin production # lihat catatan git subtree kalau pakai struktur doc+project
source venv/bin/activate
pip install -r requirements.txt # cuma perlu kalau ada dependency baru
exit
sudo systemctl restart app
restart (bukan reload) karena kode Python di-load ke memori saat proses pertama kali start — sama seperti kasus queue worker Laravel, proses lama tidak otomatis baca perubahan kode. systemctl restart perlu sudo dari user biasa (bukan fastapiapp, yang tidak punya akses sudo), makanya baris ini di luar blok sudo -iu fastapiapp.
Opsional: HTTPS
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d api.example.com
Konfigurasi SSL-nya tidak perlu ditulis manual — plugin --nginx otomatis suntik langsung ke server block yang sudah dibuat di Langkah 4 (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 api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/api.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 = api.example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name api.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.