# Laravel

> Chạy Laravel trên ZoPanel bằng trình cài một chạm hoặc Git deploy: thư mục gốc, .env và APP_KEY, migration, scheduler, queue, Redis và lưu trữ cố định.

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

Laravel chạy như một website PHP: nginx phục vụ thư mục `public` của dự án, còn PHP chạy trong pool PHP-FPM riêng của tài khoản. ZoPanel có hai cách dựng: trình cài một chạm tạo dự án Laravel mới trống, còn Git deploy build repository của bạn thành các bản release được đưa lên không gián đoạn. Dùng trình cài để thử Laravel hoặc bắt đầu dự án ngay trên máy chủ; dùng Git deploy cho ứng dụng bạn phát triển ở nơi khác.

## Yêu cầu

- Phiên bản PHP mà bản Laravel của bạn hỗ trợ, đã cài ở **Runtime**. Laravel 13 cần PHP 8.3 trở lên, Laravel 12 cần PHP 8.2 trở lên ([chính sách hỗ trợ](https://laravel.com/docs/releases#support-policy)).
- **Composer**, do quản trị viên cài ở **Runtime → Composer**.
- Gói hosting còn chỗ cho một database (MariaDB hoặc PostgreSQL, xem [Cơ sở dữ liệu](/vi/docs/databases)).
- Với Git deploy, gói phải bật **Git deploy & ứng dụng**; với trình cài, gói phải bật **Cài ứng dụng & công cụ WordPress**.

## Chọn cách triển khai

| | Trình cài một chạm | Git deploy |
| --- | --- | --- |
| Nguồn | Dự án mới từ `laravel/laravel` | Repository của bạn (hoặc file tải lên) |
| Thư mục dự án | `domains/<tên-miền>/public_html` | `domains/<tên-miền>/releases/<thời-điểm>`, `current` trỏ tới bản đang chạy |
| Thư mục gốc web | `public_html/public` | `current/public` |
| `.env` | Tạo một lần, sau đó bạn tự sửa | Sinh lại từ **Biến môi trường** mỗi lần deploy |
| Database | Tự tạo và chạy migration | Bạn tạo và khai báo trong biến môi trường |
| Cập nhật | Thủ công (Composer qua SSH) | Mỗi lần push hoặc bấm **Deploy ngay** |

## Cài một chạm

1. Tạo website **PHP** hoặc **Laravel** (**Website → Thêm website**) với PHP 8.2 trở lên.
2. Trong tab **Tổng quan**, ở thẻ **Ứng dụng**, bấm **Laravel**.
3. Nhập **Tên website** (dùng làm `APP_NAME`) rồi bấm **Cài đặt**.

ZoPanel chạy `composer create-project laravel/laravel` bằng phiên bản PHP của website, tạo database MariaDB, sinh `.env` từ `.env.example` (`APP_ENV=production`, `APP_DEBUG=false`, `APP_URL`, `DB_CONNECTION=mysql`, `DB_HOST=localhost` cùng thông tin database mới), rồi chạy `php artisan key:generate --force`, `php artisan migrate --force` và `php artisan storage:link`. Thư mục gốc web thành `public_html/public` với quy tắc rewrite **Laravel**. Chi tiết và lỗi thường gặp: [Ứng dụng PHP](/vi/docs/php-apps).

Sửa `.env` bằng [Quản lý file](/vi/docs/file-manager) hoặc qua SSH. File `.env.example` mặc định của Laravel lưu session, cache và job trong hàng đợi vào database; các bảng này được tạo ở lần migration đầu tiên.

## Deploy từ Git

1. Ở **Website → Thêm website**, chọn **Git deploy**, nhập **Địa chỉ repository** và **Nhánh**. Với website có sẵn, dùng tab **Deploy** của website.
2. ZoPanel nhận diện Laravel khi repository có file `artisan` hoặc `composer.json` yêu cầu `laravel/framework`.
3. Khai báo cấu hình trong **Biến môi trường** (xem bên dưới) rồi bấm **Lưu & deploy lại**.

Kết quả nhận diện:

| Thiết lập | Giá trị |
| --- | --- |
| Loại | **PHP (PHP-FPM)** |
| Phiên bản | Phiên bản PHP trong `require.php` của `composer.json` (bản đã cài đầu tiên đạt mức đó), nếu không có thì dùng phiên bản của website hoặc phiên bản mặc định |
| Lệnh cài đặt | `composer install --no-dev --optimize-autoloader --no-interaction` |
| Lệnh build | `php artisan storage:link --force 2>/dev/null; php artisan config:cache && php artisan route:cache && php artisan view:cache` |
| Thư mục gốc web | `public` |
| Đường dẫn lưu trữ cố định | `storage`, `.env` |

Khi `package.json` có script `build`, lệnh `npm ci && npm run build` (hoặc `npm install`) được thêm vào đầu lệnh build để biên dịch asset Vite. Khi đó máy chủ cần cài một phiên bản Node.js ở **Runtime**.

Muốn sửa lệnh, tắt **Tự nhận diện** trong thẻ **Build & chạy**, sửa trường cần đổi rồi bấm **Lưu & deploy**.

### ZoPanel tự đặt gì cho Laravel

Mỗi lần Git deploy ứng dụng Laravel, ZoPanel điền các **Biến môi trường** sau nếu bạn chưa tự đặt:

| Biến | Giá trị |
| --- | --- |
| `APP_KEY` | Khoá `base64:` ngẫu nhiên, sinh một lần rồi giữ nguyên |
| `APP_ENV` | `production` |
| `APP_DEBUG` | `false` |
| `APP_URL` | `https://<tên-miền>` (`http://` khi website chưa có SSL) |
| `LOG_CHANNEL` | `daily` |
| `QUEUE_CONNECTION` | `sync` |
| `SESSION_DRIVER`, `CACHE_STORE` | `redis` khi Redis của tài khoản dùng được, nếu không thì `file` |
| `REDIS_CLIENT`, `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `REDIS_PREFIX` | Đặt kèm driver Redis: `phpredis`, socket Redis của tài khoản, `0`, `null`, `app<id website>_` |

Log deploy ghi "Sessions and cache use the account's Redis" hoặc "Sessions and cache use files (Redis not available: …)". Muốn giữ driver riêng, hãy tự đặt `SESSION_DRIVER`; khi đó ZoPanel không đụng tới thiết lập session, cache và Redis.

**Quan trọng:** giữ nguyên `APP_KEY` khi ứng dụng đã chạy thật. Đổi khoá sẽ đăng xuất mọi người dùng và không giải mã được dữ liệu đã mã hoá.

## Biến môi trường và .env

Với Git deploy, `.env` là file lưu trữ cố định tại `domains/<tên-miền>/shared/.env`, được liên kết vào mọi bản release. Mỗi lần deploy, các **Biến môi trường** được **gộp vào file này**: dòng nào đặt một biến của panel thì được cập nhật tại chỗ, biến còn thiếu được thêm vào cuối, còn mọi dòng (và chú thích) bạn tự thêm đều được giữ nguyên. Vì vậy:

- khai báo thông tin database, email, API key trong **Biến môi trường**, hoặc dán cả file bằng **Dán .env**;
- bạn cũng có thể sửa `shared/.env` trong **Quản lý file**: các dòng của bạn vẫn còn sau lần deploy sau, nhưng biến nào cũng được đặt trong panel sẽ lấy giá trị của panel. Xoá một biến trong panel không xoá dòng của nó trong `.env`; hãy xoá cả ở đó;
- lệnh build chạy `php artisan config:cache`, nên biến vừa đổi chỉ có hiệu lực sau khi deploy lại. **Lưu & deploy lại** làm cả hai việc.

PHP-FPM không nhận trực tiếp các biến này: Laravel đọc chúng từ `.env`, vì vậy hãy giữ `.env` trong **Đường dẫn lưu trữ cố định**.

Ví dụ cấu hình database (MariaDB, user và database tạo ở **Cơ sở dữ liệu**):

```dotenv
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=alice_shop
DB_USERNAME=alice_shop
DB_PASSWORD=mat-khau-cua-ban
```

Vì cấu hình được cache, chỉ gọi `env()` bên trong các file `config/*.php`, đúng như [tài liệu cấu hình](https://laravel.com/docs/configuration#configuration-caching) của Laravel yêu cầu.

## Migration

Git deploy không tự chạy migration. Hãy thêm vào cuối lệnh build (tắt **Tự nhận diện** trước):

```bash
php artisan storage:link --force 2>/dev/null; php artisan config:cache && php artisan route:cache && php artisan view:cache && php artisan migrate --force
```

Bước build chạy trước khi bản mới được đưa lên, trong lúc bản cũ vẫn phục vụ khách trên cùng database. Hãy viết migration sao cho bản đang chạy vẫn hoạt động được (thêm cột trước khi code dùng tới, xoá cột ở bản phát hành sau).

## Storage và đường dẫn lưu trữ cố định

- `storage` được dùng chung giữa các bản release: file tải lên trong `storage/app/public`, log và cache của framework được giữ qua mọi lần deploy. Ở lần deploy đầu, thư mục `storage` trong repository được sao chép sang `shared/storage`.
- `php artisan storage:link` (trong lệnh build) tạo `public/storage`, nên file lưu trên disk `public` được nginx phục vụ trực tiếp ([tài liệu filesystem](https://laravel.com/docs/filesystem#the-public-disk)).
- Thư mục khác cần giữ lại giữa các bản, ví dụ `public/uploads`, hãy thêm vào **Đường dẫn lưu trữ cố định**.

## Một bản release được đưa lên như thế nào

1. Code được lấy về thư mục mới trong `releases/`, `storage` và `.env` được liên kết vào.
2. Lệnh cài đặt và lệnh build chạy dưới quyền tài khoản hosting, trong giới hạn CPU và RAM của tài khoản.
3. `current` được chuyển sang bản mới trong một bước, thư mục gốc web là `current/public`, và PHP-FPM được reload nhẹ nhàng.

Nếu cài đặt hoặc build lỗi, `current` giữ nguyên và bản cũ tiếp tục chạy. **Các bản release** giữ 5 bản build gần nhất; **Rollback** chuyển về một trong số đó (database không được rollback). Xem [Git deploy](/vi/docs/git-deploy).

## Scheduler và queue

Website PHP không có tiến trình chạy liên tục, nên hãy chạy scheduler của Laravel bằng **Cron Job** ([tài liệu scheduling](https://laravel.com/docs/scheduling#running-the-scheduler)). Tạo một job chạy mỗi phút (`* * * * *`):

```bash
cd /home/alice/domains/example.com/current && /usr/bin/php8.3 artisan schedule:run >> /dev/null 2>&1
```

Với bản cài một chạm, thay `current` bằng `public_html`; dùng đúng phiên bản PHP của website. Lệnh cron không được chứa ký tự `%`. Xem [Cron Job](/vi/docs/cron-jobs).

Job trong hàng đợi:

- **Git deploy** đặt `QUEUE_CONNECTION=sync`: job chạy ngay trong request, không cần worker, phù hợp với job nhẹ.
- Muốn xử lý nền, đặt `QUEUE_CONNECTION=database` (hoặc `redis`) và xử lý hàng đợi bằng scheduler hoặc một cron job, ví dụ mỗi phút:

```bash
cd /home/alice/domains/example.com/current && /usr/bin/php8.3 artisan queue:work --stop-when-empty --max-time=55 >> /dev/null 2>&1
```

`--stop-when-empty` và `--max-time` giúp worker thoát trước lần chạy kế tiếp ([tài liệu queue](https://laravel.com/docs/queues#running-the-queue-worker)). Sau mỗi lần deploy, lần chạy cron tiếp theo dùng code mới.

## Xử lý sự cố

| Hiện tượng | Cách xử lý |
| --- | --- |
| Lỗi 500 không có chi tiết | Đọc `storage/logs/laravel-*.log` (log theo ngày) bằng Quản lý file. Chỉ bật `APP_DEBUG=true` trong thời gian ngắn nếu thật cần, rồi tắt lại. |
| "No application encryption key has been specified." | `APP_KEY` đang trống. Với Git deploy, hãy deploy lại: ZoPanel sinh khoá mỗi khi `APP_KEY` trống. Với bản cài một chạm, chạy `php artisan key:generate` trong thư mục dự án. |
| Đổi biến nhưng không có tác dụng | Cấu hình được cache lúc build: bấm **Lưu & deploy lại**. |
| Sửa tay `.env` bị mất | Git deploy ghi lại `.env` từ **Biến môi trường** mỗi lần deploy. Hãy khai báo ở đó. |
| `install failed` kèm `composer: command not found` | Nhờ quản trị viên cài Composer ở **Runtime**. |
| Composer báo phiên bản PHP không thoả yêu cầu | Chọn **Phiên bản** PHP mới hơn trong **Build & chạy** (tắt **Tự nhận diện**), hoặc nâng `require.php` trong `composer.json`. |
| File tải lên biến mất sau khi deploy | Lưu vào disk `public` (trong `storage`) hoặc thêm thư mục đó vào **Đường dẫn lưu trữ cố định**. |
| `SQLSTATE[HY000] [1045] Access denied` | Đối chiếu `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` với **Cơ sở dữ liệu**. |
| Tác vụ định kỳ không chạy | Kiểm tra đường dẫn trong cron (`current` với Git deploy) và file PHP khớp phiên bản đã cài. |

## Xem thêm

- [Git deploy](/vi/docs/git-deploy)
- [Ứng dụng PHP](/vi/docs/php-apps)
- [Website và PHP](/vi/docs/hosting)
- [Cron Job](/vi/docs/cron-jobs)
- [Cơ sở dữ liệu](/vi/docs/databases)
- [Tài liệu deploy Laravel](https://laravel.com/docs/deployment)
