# Laravel

> Run Laravel on ZoPanel with the one-click installer or Git deploy: document root, .env and APP_KEY, migrations, scheduler, queues, Redis and persistent storage.

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

Laravel runs as a PHP website: nginx serves the project's `public` folder and PHP runs in the account's own PHP-FPM pool. ZoPanel offers two ways to set it up: the one-click installer creates a new, empty Laravel project, and Git deploy builds your own repository into releases that go live with no downtime. Use the installer to try Laravel or start a project on the server; use Git deploy for an application you develop elsewhere.

## Prerequisites

- A PHP version supported by your Laravel version, installed under **Runtimes**. Laravel 13 needs PHP 8.3 or newer, Laravel 12 PHP 8.2 or newer ([support policy](https://laravel.com/docs/releases#support-policy)).
- **Composer**, installed by an administrator under **Runtimes → Composer**.
- A free database in the package (MariaDB or PostgreSQL, see [Databases](/docs/databases)).
- For Git deploy, the package must include **Git deploy & apps**; for the installer, **App installer & WordPress tools**.

## Choose a setup

| | One-click installer | Git deploy |
| --- | --- | --- |
| Source | New project from `laravel/laravel` | Your repository (or uploaded files) |
| Project folder | `domains/<domain>/public_html` | `domains/<domain>/releases/<timestamp>`, `current` points to the live one |
| Document root | `public_html/public` | `current/public` |
| `.env` | Written once, then edited by you | Generated from **Environment variables** on every deploy |
| Database | Created and migrated for you | You create it and set the variables |
| Updates | By hand (Composer over SSH) | Every push or **Deploy now** |

## One-click install

1. Create a **PHP** or **Laravel** website (**Websites → New website**) with PHP 8.2 or newer.
2. On its **Overview** tab, in the **Applications** card, click **Laravel**.
3. Enter the **Site title** (used as `APP_NAME`) and click **Install**.

ZoPanel runs `composer create-project laravel/laravel` with the website's PHP version, creates a MariaDB database, writes `.env` from `.env.example` (`APP_ENV=production`, `APP_DEBUG=false`, `APP_URL`, `DB_CONNECTION=mysql`, `DB_HOST=localhost` and the new database credentials), then runs `php artisan key:generate --force`, `php artisan migrate --force` and `php artisan storage:link`. The document root becomes `public_html/public` with the **Laravel** rewrite rules. Details and errors: [PHP applications](/docs/php-apps).

Edit `.env` with the [File Manager](/docs/file-manager) or over SSH. Laravel's default `.env.example` keeps sessions, cache and queued jobs in the database; the tables are created by the first migration.

## Deploy from Git

1. In **Websites → New website**, choose **Git deploy** and enter the **Repository URL** and **Branch**. For an existing website, use its **Deploy** tab.
2. ZoPanel detects Laravel when the repository has `artisan` or requires `laravel/framework` in `composer.json`.
3. Add your settings in **Environment variables** (see below) and click **Save & redeploy**.

What detection proposes:

| Setting | Value |
| --- | --- |
| Type | **PHP (PHP-FPM)** |
| Version | The PHP version from `require.php` in `composer.json` (the first installed version at least that high), otherwise the website's or the default version |
| Install command | `composer install --no-dev --optimize-autoloader --no-interaction` |
| Build command | `php artisan storage:link --force 2>/dev/null; php artisan config:cache && php artisan route:cache && php artisan view:cache` |
| Document root | `public` |
| Persistent paths | `storage`, `.env` |

When `package.json` has a `build` script, `npm ci && npm run build` (or `npm install`) is added in front of the build command so Vite assets are compiled. A Node.js version must then be installed under **Runtimes**.

To change a command, turn **Auto-detect** off in the **Build & run** card, edit the field and click **Save & deploy**.

### What ZoPanel sets for Laravel

On every Git deploy of a Laravel app, ZoPanel fills in these **Environment variables** if you have not set them yourself:

| Variable | Value |
| --- | --- |
| `APP_KEY` | A random `base64:` key, generated once and kept |
| `APP_ENV` | `production` |
| `APP_DEBUG` | `false` |
| `APP_URL` | `https://<domain>` (`http://` while the website has no SSL) |
| `LOG_CHANNEL` | `daily` |
| `QUEUE_CONNECTION` | `sync` |
| `SESSION_DRIVER`, `CACHE_STORE` | `redis` when the account's Redis is available, otherwise `file` |
| `REDIS_CLIENT`, `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `REDIS_PREFIX` | Set with the Redis driver: `phpredis`, the account's Redis socket, `0`, `null`, `app<site id>_` |

The deploy log says "Sessions and cache use the account's Redis" or "Sessions and cache use files (Redis not available: …)". To keep your own drivers, set `SESSION_DRIVER` yourself; ZoPanel then leaves sessions, cache and Redis settings alone.

**Important:** keep `APP_KEY` once the app is live. Changing it signs every user out and makes encrypted data unreadable.

## Environment and .env

With Git deploy, `.env` is a persistent file in `domains/<domain>/shared/.env`, linked into every release. On each deploy the **Environment variables** are **merged into it**: a line that sets one of them is updated in place, missing ones are added at the end, and every other line (and comment) you added by hand is kept. So:

- set database credentials, mail settings and API keys in **Environment variables**, or paste a whole file with **Paste .env**;
- you can also edit `shared/.env` in the **File Manager**: your own lines survive the next deploy, but a variable that is also set in the panel takes the panel's value. Deleting a variable in the panel does not remove its line from `.env`; delete it there too;
- the build command runs `php artisan config:cache`, so a changed variable takes effect only after a redeploy. **Save & redeploy** does both.

PHP-FPM does not receive the variables directly: Laravel reads them from `.env`, so keep `.env` in **Persistent paths**.

Example database settings (MariaDB, the user and database from **Databases**):

```dotenv
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=alice_shop
DB_USERNAME=alice_shop
DB_PASSWORD=your-password
```

Because the configuration is cached, call `env()` only inside `config/*.php` files, as Laravel's [configuration docs](https://laravel.com/docs/configuration#configuration-caching) require.

## Migrations

Git deploy does not run migrations by itself. Add them to the end of the build command (turn **Auto-detect** off first):

```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
```

The build runs before the new release goes live, so the old release keeps serving visitors while the migration runs against the same database. Write migrations that the running release can tolerate (add columns before code uses them, remove them in a later release).

## Storage and persistent paths

- `storage` is shared between releases: uploads in `storage/app/public`, logs and framework caches survive every deploy. On the first deploy, the repository's `storage` folder is copied to `shared/storage`.
- `php artisan storage:link` (in the build command) creates `public/storage`, so files stored on the `public` disk are served by nginx ([filesystem docs](https://laravel.com/docs/filesystem#the-public-disk)).
- Add other folders that must survive a release, such as `public/uploads`, to **Persistent paths**.

## How a release goes live

1. The code is fetched into a new folder under `releases/`, with `storage` and `.env` linked in.
2. Install and build commands run as the hosting account, inside its CPU and memory limits.
3. `current` is switched to the new release in one step, the website's document root is `current/public`, and PHP-FPM is reloaded gracefully.

If install or build fails, `current` is not changed and the previous release keeps running. **Releases** keeps the last 5 builds; **Rollback** switches back to one of them (the database is not rolled back). See [Git deploy](/docs/git-deploy).

## Scheduler and queues

PHP websites have no long-running process, so run Laravel's scheduler from **Cron Jobs** ([scheduling docs](https://laravel.com/docs/scheduling#running-the-scheduler)). Create a job that runs every minute (`* * * * *`):

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

Use `public_html` instead of `current` for a one-click install, and the PHP version of the website. Cron commands may not contain `%`. See [Cron jobs](/docs/cron-jobs).

Queued jobs:

- **Git deploy** sets `QUEUE_CONNECTION=sync`: jobs run immediately inside the request. This needs no worker and suits light jobs.
- For background processing, set `QUEUE_CONNECTION=database` (or `redis`) and process the queue from the scheduler or a cron job, for example every minute:

```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` and `--max-time` make the worker exit before the next run starts ([queue docs](https://laravel.com/docs/queues#running-the-queue-worker)). After a deploy, the next cron run uses the new code.

## Troubleshooting

| Symptom | What to do |
| --- | --- |
| 500 error with no details | Read `storage/logs/laravel-*.log` (daily log) in the File Manager. Set `APP_DEBUG=true` briefly only if you must, then set it back. |
| "No application encryption key has been specified." | `APP_KEY` is empty. With Git deploy, redeploy: ZoPanel generates a key whenever `APP_KEY` is empty. For a one-click install, run `php artisan key:generate` in the project folder. |
| A changed variable has no effect | The configuration is cached at build time: click **Save & redeploy**. |
| Manual edits to `.env` disappear | Git deploy rewrites `.env` from **Environment variables** on every deploy. Put the setting there. |
| `install failed` with `composer: command not found` | Ask the administrator to install Composer under **Runtimes**. |
| Composer reports the PHP version does not satisfy a requirement | Select a newer PHP **Version** in **Build & run** (turn **Auto-detect** off), or raise `require.php` in `composer.json`. |
| Uploaded files vanish after a deploy | Store them on the `public` disk (in `storage`) or add their folder to **Persistent paths**. |
| `SQLSTATE[HY000] [1045] Access denied` | Check `DB_DATABASE`, `DB_USERNAME` and `DB_PASSWORD` against **Databases**. |
| Scheduled tasks never run | Check the cron path (`current` for Git deploy) and that the PHP binary matches an installed version. |

## Related

- [Git deploy](/docs/git-deploy)
- [PHP applications](/docs/php-apps)
- [Websites and PHP](/docs/hosting)
- [Cron jobs](/docs/cron-jobs)
- [Databases](/docs/databases)
- [Laravel deployment docs](https://laravel.com/docs/deployment)
