# Ứng dụng Python

> Deploy ứng dụng Django, Flask và FastAPI với môi trường ảo, worker gunicorn hoặc uvicorn theo cấu hình gói, file tĩnh, migration và log.

Source: https://zopanel.net/vi/docs/python  
Updated: 2026-10-09

ZoPanel chạy ứng dụng web Python như một dịch vụ của tài khoản hosting, phía sau nginx. Mỗi bản release có môi trường ảo riêng trong `.venv`, app server (gunicorn hoặc uvicorn) được cài thêm nếu thiếu, và số worker tính theo CPU, RAM của gói. Trang này nói về những điểm riêng của Python; quy trình deploy chung được mô tả ở [Git deploy](/vi/docs/git-deploy).

## Điều kiện cần

- Máy chủ có bộ công cụ Python. Trình cài đặt mặc định đã cài; nếu chưa, quản trị viên mở **Runtime**, tìm thẻ **Python** ở mục **Runtime khác** và bấm **Cài đặt**. Thao tác này cài `python3`, `python3-venv`, `python3-pip`, `python3-dev`, `build-essential`, `libpq-dev` và `pkg-config`.
- Một website loại **Git deploy**, hoặc bất kỳ website nào có tab **Deploy**.
- Gói hosting của tài khoản phải có **Git deploy & ứng dụng**.

### Phiên bản Python

Mặc định ứng dụng dùng Python 3 đi kèm hệ điều hành:

| Hệ điều hành | Python |
| --- | --- |
| Ubuntu 22.04 | 3.10 |
| Ubuntu 24.04 | 3.12 |
| Debian 12 | 3.11 |
| Debian 13 | 3.13 |

Để build ứng dụng bằng Python khác, quản trị viên cài **uv** ở mục **Runtime** (thẻ **uv (Python versions)**). ZoPanel tải bản phát hành của uv và kiểm tra nó với checksum SHA-256 được công bố kèm theo. Sau đó:

- Ô **Phiên bản** của ứng dụng chọn Python. Tự nhận diện lấy giá trị từ `.python-version` (`3.12` hay `3.12.4` đều là 3.12); khi tắt **Tự nhận diện**, chọn ở **Phiên bản** (có từ 3.10 đến 3.14).
- Trong lúc build, uv cài Python đó cho tài khoản hosting, dưới quyền tài khoản đó, trong thư mục home của nó (khoảng 100 MB mỗi phiên bản, tải một lần và dùng lại cho các lần build sau). uv kiểm tra mỗi lần tải với checksum được ghi sẵn trong uv. Sau đó lệnh `python3 -m venv .venv` của lệnh cài đặt tạo môi trường ảo bằng Python này.
- Log build ghi rõ Python được dùng, ví dụ "Using Python 3.13 (managed by uv; the system Python is 3.12)" hoặc "Using the system Python 3.12".

Khi không có uv, `.python-version` yêu cầu phiên bản khác sẽ bị bỏ qua và log build ghi rõ: "Python 3.13 was requested (.python-version or Version), but only the system Python 3.12 is available: building with 3.12…". Các giá trị uv không cài được (ví dụ `pypy3.10`) cũng bị bỏ qua kèm ghi chú. Với trình thông dịch mà cả hai cách đều không cung cấp, hãy deploy bằng Dockerfile (xem [Deploy bằng Docker](/vi/docs/docker-deploy)).

## Nhận diện

Repository được xem là Python khi có `manage.py`, `requirements.txt`, `pyproject.toml` hoặc `Pipfile`, và không có các file được kiểm tra trước: `package.json`, `composer.json`, `Gemfile`, `pom.xml`/`build.gradle` hoặc file `*.csproj`.

Ứng dụng web Python có thêm `package.json` cho công cụ front-end (Tailwind, Vite, esbuild) vẫn được nhận diện là Python khi thỏa cả hai điều kiện:

- repository có `manage.py`, hoặc `requirements.txt`, `pyproject.toml` hay `Pipfile` có nhắc tới `django`, `flask`, `fastapi`, `uvicorn` hoặc `gunicorn`;
- `package.json` không có script `start` và không dùng framework server Node nào (Next.js, Nuxt, NestJS, Express, Fastify, Koa, Hono, Remix, SvelteKit, Astro).

Nếu `package.json` có script `build`, tài nguyên front-end được build trước: `npm ci && npm run build` (hoặc `npm install && npm run build` khi không có `package-lock.json`) được đặt trước lệnh build, và Node.js phải được cài ở mục **Runtime**. Ngược lại, repository sẽ bị nhận diện là Node.js: hãy tắt **Tự nhận diện**, đặt **Runtime** là `python` và tự điền các lệnh bên dưới.

### Lệnh cài đặt

| File tìm thấy | Lệnh cài đặt |
| --- | --- |
| `requirements.txt` | `python3 -m venv .venv && .venv/bin/pip install --upgrade pip -q && .venv/bin/pip install -r requirements.txt` |
| `pyproject.toml` | `python3 -m venv .venv && .venv/bin/pip install --upgrade pip -q && .venv/bin/pip install .` |
| `Pipfile` | `python3 -m venv .venv && .venv/bin/pip install pipenv -q && .venv/bin/pipenv install --deploy --system` |

Nếu file thư viện không nhắc tới app server mà framework cần, hệ thống nối thêm ` && .venv/bin/pip install gunicorn` (Django, Flask) hoặc ` && .venv/bin/pip install uvicorn` (FastAPI). Cache tải về của pip được giữ trong tài khoản nên các lần build sau nhanh hơn.

### Framework và lệnh chạy

| Nhận diện khi | Framework | Lệnh chạy đề xuất |
| --- | --- | --- |
| có `manage.py`, hoặc `django` trong danh sách thư viện | Django | `.venv/bin/gunicorn <project>.wsgi:application --bind 127.0.0.1:$PORT` |
| `fastapi` trong danh sách thư viện | FastAPI | `.venv/bin/uvicorn <module>:<app> --host 127.0.0.1 --port $PORT` |
| `flask` trong danh sách thư viện | Flask | `.venv/bin/gunicorn <module>:<app> --bind 127.0.0.1:$PORT` |
| còn lại | Python | `.venv/bin/python main.py` (hoặc `app.py`, `server.py`) |

- **Django:** `<project>` là thư mục đầu tiên chứa `wsgi.py` (nếu không có thì là `config`). Lệnh build là `.venv/bin/python manage.py collectstatic --noinput || true`.
- **FastAPI và Flask:** ZoPanel tìm dòng như `app = FastAPI(` hoặc `app = Flask(` trong `main.py`, `app.py`, `server.py`, `app/main.py`, `src/main.py`, `wsgi.py` hoặc `api.py`, rồi dùng dạng `module:biến` (ví dụ `app.main:app`). Không tìm thấy thì dùng `main:app`.
- Nếu có `Procfile` với dòng `web:`, dòng đó luôn được dùng làm lệnh chạy.

Bấm **Phân tích repository** trong tab **Deploy** để kiểm tra kết quả trước khi deploy.

## Deploy ứng dụng

1. Vào **Website → Thêm website**, chọn **Git deploy**, nhập **Địa chỉ repository**, **Nhánh** và, nếu ứng dụng nằm trong thư mục con, **Thư mục gốc**. Bấm **Tạo website**.
2. Thêm cấu hình (URL database, secret key, allowed hosts) ở **Biến môi trường** trong tab **Deploy** rồi bấm **Lưu & deploy lại**.
3. Muốn đổi lệnh, tắt **Tự nhận diện** trong **Build & chạy**, sửa lệnh rồi bấm **Lưu & deploy**.

## Môi trường khi chạy

Lệnh chạy được thực thi từ thư mục release với:

| Biến | Giá trị |
| --- | --- |
| `PORT` | Cổng của slot. Gắn server vào `127.0.0.1:$PORT`. |
| `HOST` | `127.0.0.1` |
| `VIRTUAL_ENV`, `PATH` | `.venv` của bản release, đứng đầu `PATH`, nên `gunicorn` và `python` trỏ vào môi trường ảo. |
| `PYTHONUNBUFFERED` | `1`, để `print()` và logging hiện ra ngay trong log. |
| `WEB_CONCURRENCY` | Số worker, trừ khi bạn tự đặt: 2 × số CPU gói cho phép + 1, tối đa một worker cho mỗi 128 MB RAM của gói (tối thiểu 2), không quá 32. |

Cả [gunicorn](https://docs.gunicorn.org/en/stable/settings.html#workers) và [uvicorn](https://www.uvicorn.dev/settings/) đều lấy `WEB_CONCURRENCY` làm số worker mặc định. Tuỳ chọn `--workers` trong lệnh chạy được ưu tiên hơn.

### Biến môi trường

Các biến trong thẻ **Biến môi trường** dùng được trong lệnh cài đặt, lệnh build và khi chạy (tối đa 200 biến, giá trị một dòng). Dùng **Dán .env** để thêm nhiều biến một lúc. Đọc biến bằng `os.environ`:

```python
import os
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = os.environ.get("DJANGO_DEBUG", "") == "1"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",")
```

## Django phía sau nginx

nginx xử lý SSL và chuyển request kèm header `X-Forwarded-Proto`. Thêm vào `settings.py`:

```python
ALLOWED_HOSTS = ["example.com", "www.example.com"]
CSRF_TRUSTED_ORIGINS = ["https://example.com", "https://www.example.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
```

Thiếu `CSRF_TRUSTED_ORIGINS`, form gửi qua HTTPS sẽ bị lỗi 403 CSRF.

### File tĩnh

Với website proxy, mọi request, kể cả `/static/`, đều đi tới ứng dụng. Hãy phục vụ file tĩnh ngay trong ứng dụng bằng [WhiteNoise](https://whitenoise.readthedocs.io/):

1. Thêm `whitenoise` vào `requirements.txt`.
2. Thêm `"whitenoise.middleware.WhiteNoiseMiddleware"` ngay sau `SecurityMiddleware` trong `MIDDLEWARE`.
3. Đặt `STATIC_ROOT = BASE_DIR / "staticfiles"`.

Lệnh build được nhận diện chạy `collectstatic` ở mỗi lần deploy. Lệnh kết thúc bằng `|| true` nên lỗi không làm dừng deploy: nếu trang mất CSS, hãy xem log build ở **Tác vụ**.

### File người dùng tải lên

File ghi vào thư mục release sẽ mất ở lần deploy sau. Hãy thêm thư mục media vào **Đường dẫn lưu trữ cố định** (ví dụ `media`), hoặc lưu file tải lên vào S3 bằng `django-storages` và [Lưu trữ S3](/vi/docs/apps) của ZoPanel. Khi `DEBUG` tắt, Django không tự phục vụ `MEDIA_ROOT`, nên nếu dùng thư mục lưu cố định bạn cần thêm view hoặc cấu hình WhiteNoise để phục vụ nó.

## Migration

ZoPanel không tự chạy migration. Hãy thêm vào **Lệnh build**, lệnh này chạy cùng biến môi trường của bạn:

```bash
.venv/bin/python manage.py collectstatic --noinput && .venv/bin/python manage.py migrate --noinput
```

Với Flask-Migrate dùng `.venv/bin/flask db upgrade`, với Alembic dùng `.venv/bin/alembic upgrade head`.

**Quan trọng:** migration chạy trước khi bản mới đạt health check, trong lúc bản cũ vẫn đang phục vụ, và **Rollback** không hoàn tác migration. Hãy giữ migration tương thích ngược (thêm cột trước, xoá cột ở bản sau).

## Phát hành không gián đoạn, rollback và log

Như mọi ứng dụng server, bản release Python khởi động ở slot rảnh và chỉ nhận lưu lượng khi **Đường dẫn health check** (mặc định `/`) trả về mã dưới 500 trong vòng 90 giây. Mã 400 hay 404 vẫn đạt; mã 500 thì không. Thẻ **Các bản release** giữ 5 bản build gần nhất để **Rollback**.

- **Log ứng dụng** trong tab **Deploy** hiển thị 400 dòng mới nhất; bật **Trực tiếp** để theo dõi.
- Log build nằm ở **Tác vụ**; log nginx ở tab **Nhật ký**.

Mỗi bản release có `.venv` riêng, được tạo lại ở mỗi lần deploy. Hãy lưu dữ liệu ở database hoặc đường dẫn lưu trữ cố định, đừng lưu trong `.venv` hay thư mục release.

## Xử lý sự cố

| Thông báo hoặc hiện tượng | Nguyên nhân và cách xử lý |
| --- | --- |
| `the app did not respond on port … within 90s (it must listen on $PORT)` | Server gắn vào địa chỉ hoặc cổng khác. Dùng `--bind 127.0.0.1:$PORT` (gunicorn) hoặc `--host 127.0.0.1 --port $PORT` (uvicorn). |
| `the app exited during startup` | Xem **Log ứng dụng**. Nguyên nhân thường gặp: sai `module:app` trong lệnh chạy, thiếu biến môi trường, lỗi import. |
| `install failed: …` kèm `No module named venv` hoặc `ensurepip is not available` | Thiếu bộ công cụ Python. Quản trị viên cài **Python** ở **Runtime**. |
| Log build ghi "only the system Python … is available" | Dự án yêu cầu Python khác trong `.python-version`. Nhờ quản trị viên cài **uv** ở mục **Runtime**, hoặc xóa `.python-version` nếu Python của hệ thống là đủ. |
| `could not install Python 3.x with uv` | uv không tải được Python đó (không có mạng, phiên bản không tồn tại, hoặc tài khoản đã hết dung lượng đĩa). Đọc các dòng phía trên trong log build. |
| `install failed: …` khi build `mysqlclient` | Chưa cài header của MySQL client. Dùng `PyMySQL`, hoặc nhờ quản trị viên chạy `apt-get install -y default-libmysqlclient-dev pkg-config`. |
| `Failed to find attribute 'app' in 'main'` | Đối tượng ứng dụng có tên hoặc module khác. Sửa lệnh chạy, ví dụ `.venv/bin/gunicorn myapp.wsgi:app --bind 127.0.0.1:$PORT`. |
| Django trả về `Bad Request (400)` với tên miền | Thêm tên miền vào `ALLOWED_HOSTS`. |
| Trang không có CSS | File tĩnh chưa được phục vụ: cài WhiteNoise và kiểm tra `collectstatic` chạy thành công trong log build. |
| Ứng dụng bị dừng khi tải cao | Quá nhiều worker so với RAM của gói. Đặt `WEB_CONCURRENCY` thấp hơn trong **Biến môi trường**. |

## Xem thêm

- [Git deploy](/vi/docs/git-deploy)
- [Ứng dụng Node.js và Python](/vi/docs/apps-node-python)
- [Cơ sở dữ liệu](/vi/docs/databases)
- [Gói hosting và giới hạn](/vi/docs/packages-limits)
- [Checklist triển khai Django](https://docs.djangoproject.com/en/stable/howto/deployment/checklist/)
- [Triển khai FastAPI](https://fastapi.tiangolo.com/deployment/)
