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
- Create a PHP or Laravel website (Websites → New website) with PHP 8.2 or newer.
- On its Overview tab, in the Applications card, click Laravel.
- 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
- In Websites → New website, choose Git deploy and enter the Repository URL and Branch. For an existing website, use its Deploy tab.
- ZoPanel detects Laravel when the repository has
artisanor requireslaravel/frameworkincomposer.json. - 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/.envin 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
storageis shared between releases: uploads instorage/app/public, logs and framework caches survive every deploy. On the first deploy, the repository'sstoragefolder is copied toshared/storage.php artisan storage:link(in the build command) createspublic/storage, so files stored on thepublicdisk 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
- The code is fetched into a new folder under
releases/, withstorageand.envlinked in. - Install and build commands run as the hosting account, inside its CPU and memory limits.
currentis switched to the new release in one step, the website's document root iscurrent/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(orredis) 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. |