Kembali ke blog
Agustus 19, 2026Panduan

Cara Menjalankan Aplikasi Python dengan Gunicorn dan Nginx di VPS

Deploy aplikasi Flask atau FastAPI di Ubuntu dengan virtualenv, layanan systemd Gunicorn, reverse proxy Nginx, TLS, berkas lingkungan, pencatatan, dan daftar periksa untuk kesalahan 502 serta ukuran worker.

Cara Menjalankan Aplikasi Python dengan Gunicorn dan Nginx di VPS

Server Flask bawaan dan uvicorn --reload untuk pengembangan. Di VPS publik Anda ingin pengelola proses yang merestart worker, mengikat ke localhost, dan duduk di belakang reverse proxy yang menangani TLS serta klien lambat. Gunicorn adalah pilihan WSGI biasa untuk Flask dan Django. FastAPI dapat berjalan di bawah Gunicorn dengan kelas worker Uvicorn. Nginx (atau Caddy) mengakhiri HTTPS dan meneruskan ke 127.0.0.1:8000.

Panduan ini menelusuri tata letak yang bertahan setelah reboot: proyek di /srv/app, virtualenv, .env dengan rahasia, unit gunicorn.service, blok server Nginx, Let's Encrypt, dan berkas log yang benar-benar bisa Anda grep. Kita juga akan membahas jumlah worker, timeout, dan mengapa 502 Bad Gateway hampir tidak pernah 'Nginx rusak' — hampir selalu Gunicorn tidak berjalan, terikat ke soket yang salah, atau crash saat impor. Contoh memakai Flask, dengan catatan FastAPI di mana perintah berbeda.

Mengapa tumpukan ini

Gunicorn melakukan prefork proses worker. Setiap worker menangani satu permintaan pada satu waktu kecuali Anda memakai kelas worker berbeda. Nginx menyangga klien lambat agar worker tidak macet mengirim byte ke jaringan seluler. systemd merestart aplikasi jika ia mati. Bersama-sama ini membosankan, yang merupakan apa yang Anda inginkan pukul 3 pagi.

  • Gunicorn: model proses WSGI/ASGI yang stabil
  • systemd: mulai saat boot, restart saat crash, log journald
  • Nginx: TLS, berkas statis, batas ukuran permintaan, gzip
  • venv: Python sistem tetap bersih
  • ikatan localhost: aplikasi tidak dapat dijangkau kecuali lewat Nginx

Persyaratan

Python 3.10+ di Ubuntu 22.04/24.04 baik-baik saja. Jangan jalankan pip sebagai root ke site-packages sistem. Anda butuh domain untuk TLS. Jika Anda lebih suka Caddy sebagai proxy, unit Gunicorn di artikel ini tetap sama — hanya konfigurasi front-end yang berubah (lihat artikel Caddy).

  • VPS Ubuntu 22.04 atau 24.04
  • Aplikasi Anda dengan requirements.txt atau setara
  • Titik masuk WSGI (untuk Flask: app:app) atau ASGI (untuk FastAPI: app:app dengan worker uvicorn)
  • Rekaman A domain untuk HTTPS

Langkah 1: Paket sistem, pengguna, dan direktori proyek

Buat pengguna sistem yang tidak bisa login interaktif, miliki kode, dan jalankan Gunicorn. Menginstal python3-venv dan alat build menghindari kegagalan pip pada paket yang masih mengompilasi ekstensi C.

bash
ssh root@YOUR_VPS_IP
apt update && apt -y upgrade
apt -y install python3 python3-venv python3-pip python3-dev build-essential nginx curl

adduser --system --group --home /srv/app appuser
mkdir -p /srv/app
chown appuser:appuser /srv/app

# Copy your code (example):
# rsync -a --delete ./myproject/ appuser@YOUR_VPS_IP:/srv/app/

Langkah 2: Virtualenv dan dependensi

Buat venv sebagai appuser agar kepemilikan berkas benar. Sematkan versi di produksi. Setelah instal, pastikan Anda dapat mengimpor aplikasi dalam gunicorn --check-config sekali atau impor Python. Kesalahan impor di sini adalah kesalahan yang sama yang menjadi 502 kemudian.

bash
sudo -u appuser -H bash -lc '
cd /srv/app
python3 -m venv /srv/app/venv
/srv/app/venv/bin/pip install --upgrade pip
/srv/app/venv/bin/pip install -r /srv/app/requirements.txt gunicorn
'

# Flask example check:
sudo -u appuser -H /srv/app/venv/bin/python -c "from app import app; print('import ok')"

Langkah 3: Berkas lingkungan

Jangan hardcode SECRET_KEY atau URL basis data di unit systemd dengan cara yang berakhir di git yang dapat dibaca dunia. Gunakan EnvironmentFile. chmod 640, pemilik root, grup appuser (atau pemilik appuser jika Anda lebih suka).

bash
cat >/srv/app/.env <<'EOF'
FLASK_ENV=production
SECRET_KEY=replace-with-openssl-rand-hex-32
DATABASE_URL=postgresql://app:password@127.0.0.1:5432/app
EOF
chown appuser:appuser /srv/app/.env
chmod 600 /srv/app/.env

Langkah 4: Layanan systemd Gunicorn

Ikat ke 127.0.0.1:8000, bukan 0.0.0.0, kecuali Anda punya alasan melewati Nginx. Jumlah worker sering (2 x CPU) + 1 untuk worker sinkron; di VPS 2 vCPU itu 5, yang mungkin terlalu banyak jika setiap worker memuat model ML berat — lalu pakai 2–3. Untuk FastAPI, atur --worker-class uvicorn.workers.UvicornWorker dan instal uvicorn. Timeout harus melebihi permintaan jujur paling lambat Anda, bukan 30 detik jika Anda punya ekspor 2 menit.

bash
cat >/etc/systemd/system/gunicorn.service <<'EOF'
[Unit]
Description=Gunicorn for the web app
After=network.target

[Service]
User=appuser
Group=appuser
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/srv/app/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 --timeout 60 --access-logfile - --error-logfile - app:app
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now gunicorn
systemctl status gunicorn --no-pager
ss -tulpn | grep 8000

Langkah 5: Reverse proxy Nginx dan TLS

Nginx mendengar di 80/443 dan memproksi ke Gunicorn. client_max_body_size penting untuk unggahan. proxy_read_timeout harus cocok atau melebihi timeout Gunicorn. Setelah blok server bekerja di HTTP, terbitkan sertifikat dengan Certbot (atau ganti front ke Caddy). Cuplikan di bawah hanya HTTP agar Anda bisa menguji; lalu jalankan Certbot yang dapat mengedit berkas.

bash
cat >/etc/nginx/sites-available/app <<'EOF'
server {
    listen 80;
    server_name app.example.com;
    client_max_body_size 20m;

    location /static/ {
        alias /srv/app/static/;
    }

    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;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 90s;
    }
}
EOF

ln -sf /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
nginx -t && systemctl reload nginx

ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable

apt -y install certbot python3-certbot-nginx
certbot --nginx -d app.example.com

Langkah 6: Berkas statis, izin, dan kekhususan Flask/FastAPI

Nginx seharusnya menyajikan berkas statis jika bisa; lebih cepat daripada Gunicorn. Django collectstatic, Flask send_from_directory untuk aplikasi kecil, atau CDN nanti. appuser harus bisa membaca pohon. Jika Anda memakai FastAPI, instal uvicorn[standard] di venv dan ubah ExecStart untuk memakai UvicornWorker. Jika Anda memakai soket Unix alih-alih TCP, arahkan proxy_pass ke soket dan cocokkan izin agar www-data dapat menulis ke sana.

bash
# FastAPI ExecStart example:
# ExecStart=/srv/app/venv/bin/gunicorn -k uvicorn.workers.UvicornWorker --workers 2 --bind 127.0.0.1:8000 app:app

# Unix socket variant:
# --bind unix:/run/gunicorn/app.sock
# Nginx: proxy_pass http://unix:/run/gunicorn/app.sock:

chown -R appuser:appuser /srv/app

Worker, memori, dan reload tanpa downtime

Worker Gunicorn sinkron sederhana dan cukup untuk API permintaan/respons yang ringan CPU. Jika Anda butuh banyak tunggu I/O lambat bersamaan, pertimbangkan gevent atau worker ASGI — ukur, jangan menebak. Setiap worker memuat aplikasi Anda; RAM ~= worker dikali RSS aplikasi. VPS 2 GB dengan 8 worker dari aplikasi 300 MB akan swap dan terasa 'lambat secara acak'. systemctl reload gunicorn (HUP) dapat merestart worker dengan kode baru jika Anda mendeploy berkas di tempat; restart penuh lebih jelas ketika dependensi berubah.

bash
# After git pull / rsync of new code:
sudo -u appuser -H /srv/app/venv/bin/pip install -r /srv/app/requirements.txt
systemctl restart gunicorn
curl -I https://app.example.com/health

Memecahkan 502 dan crash senyap

502 berarti Nginx tidak bisa mendapatkan respons valid dari upstream. journalctl -u gunicorn -e adalah perintah pertama. Penyebab umum: nama module:app salah, kunci .env hilang, Postgres tidak berjalan, terikat ke 127.0.0.1 tetapi Nginx di host lain, SELinux (jarang di Ubuntu), atau aplikasi hanya mendengar IPv6. 504 adalah timeout. Loop 301 terjadi ketika aplikasi mengalihkan ke HTTP sementara X-Forwarded-Proto diabaikan.

  • systemctl status gunicorn — apakah aktif?
  • journalctl -u gunicorn -n 100 — ImportError, env hilang, basis data
  • curl -v http://127.0.0.1:8000/ dari VPS — jika ini gagal, Nginx tidak bersalah
  • nginx -t dan error.log — upstream prematurely closed connection
  • ss -tulpn | grep 8000 — tidak ada yang mendengar
  • Disk penuh — worker crash dengan cara misterius

Keamanan

Aplikasi tidak pernah mengikat secara publik. Rahasia tetap di .env. Jaga venv dan OS tetap di-patch. Jangan jalankan Gunicorn sebagai root. Jika Anda menangani login, atur cookie sesi Secure dan SameSite, dan konfigurasi kerangka untuk mempercayai X-Forwarded-Proto dari Nginx saja. Batasi laju rute login di Nginx atau di aplikasi.

  • ikat 127.0.0.1 atau soket unix
  • chmod 600 .env
  • User= non-root di systemd
  • TLS lewat Certbot atau Caddy
  • Nonaktifkan mode debug dan muat ulang otomatis di produksi

Tips

  • Tambahkan rute /health yang memeriksa konektivitas DB untuk Compose dan penyeimbang beban
  • Kirim aset statis dengan header cache-control di Nginx
  • Gunakan worker atau antrean terpisah (Redis + systemd) untuk email dan pekerjaan berat
  • Sematkan versi gunicorn dan uvicorn di requirements.txt
  • Ambil snapshot sebelum cutover produksi pertama

Aplikasi Python di VPS siap produksi ketika berjalan di bawah Gunicorn sebagai layanan systemd, hanya mengikat ke localhost, dan dijangkau lewat Nginx atau Caddy dengan TLS. Taruh rahasia di EnvironmentFile, ukur worker menurut RAM bukan rumus blog, dan debug 502 dari jurnal Gunicorn dulu. Setelah jalur ini terdokumentasi untuk repositori Anda, setiap deploy berikutnya adalah rsync atau git pull, pip install, dan systemctl restart gunicorn.