Logo

~/saidqb cat wiki/stack/server/deploy-fastapi.md

MDdeploy-fastapi

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.

Contoh command di dokumen ini pakai Ubuntu/Debian (apt). Kalau server-nya distro RHEL-based (Rocky Linux/AlmaLinux/CentOS Stream, pakai dnf), lihat catatan padanan di tiap langkah yang berbeda — FastAPI murni Python, jadi (beda dari Laravel) tidak butuh repo Remi, Python 3 dari repo AppStream bawaan RHEL sudah cukup baru.

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 untuk git pull tiap deploy)
  • Text editor di server buat edit systemd unit/config, minimal nano atau vim — server diakses lewat SSH, tidak ada GUI editor: sudo apt install vim -y

Rocky Linux/AlmaLinux — padanan paket di atas:

bash
sudo dnf install python3 python3-pip nginx git vim -y

python3-venv tidak perlu dipasang terpisah — modul venv sudah ikut satu paket dengan python3 di RHEL-based (beda dari Debian/Ubuntu yang memisahnya).

1. Buat user khusus untuk app

bash
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)

bash
sudo -iu fastapiapp
git clone -b production --single-branch <url-repo-git> app   # clone hanya branch production, tanpa riwayat/branch lain
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. -b production --single-branch supaya server cuma punya branch production (bukan main + seluruh branch lain) — cocok karena Langkah 5 juga selalu git pull origin production.

3. Buat systemd service

/etc/systemd/system/app.service

ini
[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.

bash
sudo systemctl daemon-reload
sudo systemctl enable app
sudo systemctl start app

4. Reverse proxy Nginx

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;
    }
}
bash
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.

Rocky Linux/AlmaLinux: tidak ada sites-available/sites-enabled bawaan — taruh langsung file konfigurasinya di /etc/nginx/conf.d/app.conf (tanpa symlink). Firewall default-nya firewalld, bukan ufw:

bash
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload

SELinux aktif default di RHEL-based — kalau Nginx gagal proxy_pass ke 127.0.0.1:8000 padahal config & service sudah benar, biasanya SELinux yang blok koneksi keluar dari proses Nginx: sudo setsebool -P httpd_can_network_connect 1.

5. Urutan tiap kali deploy update

bash
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

bash
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d api.example.com

Rocky Linux/AlmaLinux: paket certbot-nya dari EPEL (sudo dnf install epel-release -y dulu kalau belum ada):

bash
sudo dnf 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:

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;
    }

    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.