Deploy Laravel
This guide deploys a Laravel 11, 12 or 13 app from a Git repository to one or more servers. You end with zero-downtime deployments, migrations that run once, a working scheduler, and optional queue workers or Horizon. Statamic sites use the same flow with the Statamic preset.
Prerequisites
Section titled “Prerequisites”- A connected Git provider: GitHub App or a token.
- An active server of type app or web with PHP installed (FrankenPHP is the default runtime). See Connect a server.
- Optional: a database. Create one on an app server with a database engine, or on a db server. See Create databases.
- Your repository has
composer.jsonandartisan. If it has apackage.jsonwith abuildscript (Vite), assets are built too.
Deploy
Section titled “Deploy”-
Create a database (skip if your app does not need one). On the canvas: + Create → Database, pick the engine and server. Name the service, for example
shop-db. -
Create the site. + Create → Git repository → choose your connection, repository and branch. Falak detects Laravel and selects the Laravel preset.
-
Pick servers. Select one or more servers. The first one is the leader: migrations and the scheduler run there.
-
Choose a domain. Keep Generate for an instant
sslip.ioURL, or enter your own domain. See Domains. -
Click Deploy. The panel opens on the Deployments tab and streams the first deployment.
-
Connect the database. Open the site’s Variables tab and add references to the database service:
Site variables DB_CONNECTION=${{ shop-db.DB_CONNECTION }}DB_HOST=${{ shop-db.DB_HOST }}DB_PORT=${{ shop-db.DB_PORT }}DB_DATABASE=${{ shop-db.DB_DATABASE }}DB_USERNAME=${{ shop-db.DB_USERNAME }}DB_PASSWORD=${{ shop-db.DB_PASSWORD }}Or use one line:
DB_URL=${{ shop-db.DATABASE_URL }}. References resolve at deploy time. See Variable references.If the database lives on the same app server as the site, the reference gives
DB_HOST=127.0.0.1for a native site, or the server’s own address for a Docker site (engines on app servers serve that server only). That works only while the site runs on that server alone; on other servers the deploy fails with an explanation, so use a database on adbserver and allow the app servers in its firewall. -
Redeploy to apply the variables. Variable changes always take effect on the next deployment.
What the Laravel preset sets up
Section titled “What the Laravel preset sets up”| Setting | Value |
|---|---|
| Runtime | frankenphp (switch to php-fpm if you prefer) |
| Web directory | public |
| Shared paths | storage/ (directory), .env (file) — kept across releases in shared/ |
| Health check | GET /up expecting 200 (Laravel’s built-in health route) |
| Scheduler | On |
| Horizon, Octane | Off |
| Initial variables | APP_NAME, APP_ENV=production, APP_KEY (generated base64: key), APP_DEBUG=false, APP_URL (the chosen domain), LOG_CHANNEL=daily |
The default deploy script
Section titled “The default deploy script”$FALAK_FETCH
cd "$FALAK_RELEASE_DIR"if [ "$FALAK_IS_LEADER" = "1" ]; then $FALAK_PHP artisan migrate --forcefi$FALAK_PHP artisan optimize$FALAK_PHP artisan storage:link --force
$FALAK_ACTIVATE$FALAK_RESTART_PROCS$FALAK_FETCHdownloads the built release and links shared paths.- The
ifblock runs only on the leader, so migrations run once even with ten servers. $FALAK_PHPis the PHP CLI of the site’s PHP version, for examplephp8.4.$FALAK_ACTIVATEswitchescurrenton all servers together.$FALAK_RESTART_PROCSrestarts workers, Horizon and Octane with the new release.
Edit it under Settings → Deploy. All macros and variables are in Deploy scripts.
FrankenPHP or PHP-FPM
Section titled “FrankenPHP or PHP-FPM”The server’s FrankenPHP process embeds Caddy and runs PHP in-process. There is no separate PHP-FPM. It is the default for new servers and sites, and it enables Octane in worker mode.
On FrankenPHP, PHP runs as the edge user, which joins each site’s group so it can read .env and write to storage/.
A standalone Caddy serves static files and passes .php requests to a per-site PHP-FPM pool running as the site user. Choose PHP-FPM as the server’s PHP runtime when you create the server, and php-fpm as the site runtime.
Octane on PHP-FPM servers uses Swoole or RoadRunner, which you must install yourself.
Queues
Section titled “Queues”Add queue workers under the Processes tab:
| Field | Meaning | Range |
|---|---|---|
| Connection | Queue connection (empty = default) | |
| Queue | Comma-separated queue names | |
| Processes | Number of worker processes | 1–64 |
| Timeout, Sleep, Tries, Backoff | Passed to queue:work |
|
| Max jobs, Max time | Restart a worker after N jobs / seconds | |
| Memory | --memory in MB |
32–65536 |
| Command | Replace queue:work with your own command |
|
| Servers | Run on selected servers only (default: all) |
The worker command is php8.4 artisan queue:work <connection> --queue=<queues> --sleep=<s> --tries=<n> --timeout=<s> --memory=<mb> …. Workers restart on every deployment. See Processes.
Horizon
Section titled “Horizon”Turn on Horizon under Settings → Laravel. Falak supervises php artisan horizon on each server and sends horizon:terminate on deploys so running jobs finish (with a 120-second stop timeout). Install laravel/horizon in your app first.
Scheduler
Section titled “Scheduler”The scheduler is on by default. Falak runs php artisan schedule:run every minute on the leader only, in the current release, as a cron job with a heartbeat. Missed runs show up under Insights → Heartbeats.
Laravel’s withoutOverlapping() handles overlaps; Falak never skips a minute because a previous run is still going.
Maintenance mode
Section titled “Maintenance mode”Toggle Maintenance under Settings → Laravel. Falak runs php artisan down --retry=60 (or php artisan up) on every server immediately.
New Laravel sites use LOG_CHANNEL=daily. The agent tails shared/storage/logs/*.log, merges multi-line stack traces into one record, and ships them to Loki with the site’s labels. See Logs.
Instrument with APM
Section titled “Instrument with APM”Install the Falak APM package to get request timelines, slow queries, N+1 hints, job traces and grouped exceptions:
composer require falak/apm-laravelNo configuration is needed on a Falak server. See APM for Laravel.
Run artisan commands
Section titled “Run artisan commands”Use Settings → Commands to run a one-off command such as php artisan tinker --execute="…" or php artisan cache:clear on the site’s servers, with live output. Commands time out after 600 seconds (FALAK_SITE_COMMAND_TIMEOUT).
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause and fix |
|---|---|
| Health check fails with 500 | Open View logs, then the site’s Logs tab. Usually a missing variable (database, APP_KEY) or a failed artisan optimize. |
Unresolved variable references: … |
A ${{ service.KEY }} points at a service or key that does not exist in this environment. |
Permission denied writing to storage/ |
storage/ must be a shared path (default). Redeploy so permissions are fixed. |
| Migrations ran twice | Your script runs migrations outside the FALAK_IS_LEADER block. |
| Scheduler never runs | It only runs on the leader, and only after the first successful deployment. |
| Assets missing | Your package.json needs a build script; node_modules is not shipped for PHP sites. |