Guides

PHP and Laravel

Running a Laravel app — website, queue worker and scheduler — and plain PHP sites on Meghly.

What runs

  • PHP 8.5, 8.4, 8.3 and 8.2 (the panel warns on 8.2). The version comes from config.platform.php or require.php in composer.json.
  • Laravel 10, 11, 12 — recognised by the artisan file or laravel/framework.
  • Plain PHP sites — with a composer.json or index.php. With a public/index.php, the document root is public, otherwise the repository's root.
  • Extensions: pdo_mysql, pdo_pgsql, zip, bcmath, opcache and PHP's common ones are there already. With ext-pdo_sqlsrv (or ext-sqlsrv) in composer.json, SQL Server's driver is installed too.

Preparing Laravel

Do not commit .env

Every setting comes from the panel's variables, and Laravel reads them at run time. Meghly does not run config:cache, so a changed variable takes effect after a restart.

Running behind HTTPS

The app runs behind Meghly's router. Unless Laravel is told to trust the proxy, links and redirects become http://:

bootstrap/app.php (Laravel 11/12)
->withMiddleware(function (Middleware $middleware) {
    $middleware->trustProxies(at: '*');
})

In Laravel 10: protected $proxies = '*'; in app/Http/Middleware/TrustProxies.php.

Health check

Meghly expects a 2xx on /up. Laravel 11 and later have it already; in Laravel 10 add it:

routes/web.php
Route::get('/up', fn () => response('OK'));

Front-end (Vite)

With a package.json, the build runs npm ci and npm run build, and the result goes to public/build.

Variables

Set at least these in Environment:

Environment
APP_KEY=base64:...        # on your PC: php artisan key:generate --show
APP_ENV=production
APP_DEBUG=false
APP_URL=https://shop.example.com
LOG_CHANNEL=stderr
  • Set APP_KEY once and never change it — changing it breaks encrypted data and sessions.
  • With LOG_CHANNEL=stderr the log shows on the app's Logs page.

A linked database brings DB_CONNECTION, DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD by itself — no need to touch config/database.php. A linked Redis brings REDIS_HOST, REDIS_PASSWORD … and REDIS_CLIENT=phpredis.

To send mail (port 25 is closed), use SMTP:

Environment
MAIL_MAILER=smtp
MAIL_HOST=<your mail server>
MAIL_PORT=587
MAIL_USERNAME=...
MAIL_PASSWORD=...

What Meghly runs

ProcessWhat runs
webApache + PHP, port 8080, health /up
workerphp artisan queue:work --sleep=3 --tries=3 --max-time=3600
scheduler (cron)php artisan schedule:run every minute
releasephp artisan migrate --force before every deploy

If you do not use queues, turn worker off in Processes. After every deploy the worker moves to the new version by itself. With Laravel Reverb, WebSockets work on the app's HTTPS address.

The storage folder

storage/ is a volume — uploads, cache, sessions and log files survive deploys.

php artisan storage:link cannot run while the app runs (the file system is read-only). Two ways:

  • Run php artisan storage:link --relative on your PC and commit the public/storage symlink to Git. (A symlink may not survive a zip upload.)
  • Or keep files in S3-style storage (FILESYSTEM_DISK=s3).

Bringing old data

Export from the old server:

sh
mysqldump --single-transaction --routines --triggers --no-tablespaces mydb > mydb.sql

Then in the panel, Databases → the database → Import SQL dump. The import runs inside your database — a dump naming another database (USE other_db) will not work.

When something goes wrong

What you seeCause and fix
500, and No application encryption key in the logSet APP_KEY.
http:// links or redirects, mixed contenttrustProxies(at: '*') and https:// in APP_URL.
Stops at the health check (Laravel 10)Add the /up route.
Permission denied … bootstrap/cacheDo not commit bootstrap/cache; the build makes it.
Release: could not find driver (SQL Server)Add ext-pdo_sqlsrv to composer.json.
Queued jobs do not runCheck worker is on and QUEUE_CONNECTION is right.
The scheduler does not runCheck the scheduler cron is on in Processes.

Stuck?

Talk to our engineers directly — by phone or email.

01782618248shohag.meghly@gmail.com