DocsLaravel

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.

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).
  • Composer, installed by an administrator under Runtimes → Composer.
  • A free database in the package (MariaDB or PostgreSQL, see 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.

Edit .env with the 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):

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 require.

Migrations

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

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).
  • 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.

Scheduler and queues

PHP websites have no long-running process, so run Laravel's scheduler from Cron Jobs (scheduling docs). Create a job that runs every minute (* * * * *):

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.

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:
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). 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.

← PHP applications Go apps →