
Production Preflight for Laravel
A misconfigured production application rarely fails. APP_DEBUG=true serves pages, QUEUE_CONNECTION=sync works through every job, MAIL_MAILER=log accepts every message, and a key set twice in .env is valid syntax. Nothing turns red, and the problem shows when someone goes looking for it, or when a customer calls.
This package checks for those states twice: once in the deploy, so they never go live, and then on a schedule, because half of them appear after the go-live. A value changed on the server without config:cache, workers that never heard queue:restart, a migration nobody ran.
The checks are laravel-health checks, so they report through what the application already runs: its result store, its notifications and its monitoring endpoint. The deploy gets its own command, with an exit code that stops it.
Installation
composer require pushery/production-preflight-for-laravel
The service provider is registered automatically through package discovery. The package requires spatie/laravel-health, which Composer installs alongside.
Quick start
Stop a deploy on a finding. In the deploy script, after composer install and before the application goes live:
php artisan preflight --deploy
It exits with 0 when no check failed, 1 when one did, and 2 when a check could not look. A warning is printed and lets the deploy through. Deploying has the complete script for Forge.
Report in operation. Register the checks with laravel-health, in the boot method of a service provider:
use Pushery\ProductionPreflight\Preflight;
use Spatie\Health\Facades\Health;
Health::checks(Preflight::checks());
laravel-health then runs them with health:check, which the application schedules. In operation covers the schedule and the checks that need more than the defaults.
Look yourself. On the server, without --deploy, the command runs every check and prints the report:
php artisan preflight
What it checks
Twenty-seven checks in three groups, sorted by what they need in order to answer. The checks page lists each with its verdict.
| Group | Needs | Runs |
|---|---|---|
The .env file | the file, read as text | in a pipeline, in the deploy, on a schedule |
| The configuration | the loaded configuration | in the deploy and on a schedule |
| The installation | the server itself | on a schedule only |
Each check answers with one of four verdicts: pass, warning, fail, or not checked. "Not checked" is never a pass. A check that could not look, a missing file or a fact the command line cannot see, says so, and the deploy command exits with 2 for it.
Configuration
Publish the configuration file, where every option is documented inline:
php artisan vendor:publish --tag=production-preflight-config
| Tag | Publishes |
|---|---|
production-preflight | everything below, in one command |
production-preflight-config | the configuration file |
production-preflight-lang | the translation files |
The checks report in the application's locale, in English, German, Spanish, French, Italian,
Dutch or Portuguese. A regional locale reads the catalog of its language: pt-BR and pt-PT
read pt, de-AT reads de. A file you publish for the region under
lang/vendor/production-preflight/pt-BR/ wins over the language key by key.
A setting that reads an environment variable changes without publishing the file:
| Environment variable | Setting | Default |
|---|---|---|
PRODUCTION_PREFLIGHT_ENV_FILE | production-preflight.env.file | the .env in the base path |
PRODUCTION_PREFLIGHT_ENV_EXAMPLE | production-preflight.env.example | the .env.example in the base path |
PRODUCTION_PREFLIGHT_HTTP_PROBE | production-preflight.http_probe.enabled | off |
PRODUCTION_PREFLIGHT_HTTP_PROBE_TIMEOUT | production-preflight.http_probe.timeout | 5 seconds |
PRODUCTION_PREFLIGHT_PHP_FPM | production-preflight.php.fpm | none |
PRODUCTION_PREFLIGHT_PHP_TIMEOUT | production-preflight.php.timeout | 10 seconds |
The probe switch is on only for true, 1, yes and on, in any case. A timeout that is no number, or one below 1, keeps its default.
Stages
The production rules apply in production and on staging. A staging installation often holds real customer data, and a check that listened to production alone would stay quiet exactly there. The stage is read from app.env, against the names in production-preflight.stages:
'stages' => [
'production' => ['production', 'prod', 'live'],
'staging' => ['staging', 'stage', 'preprod', 'uat'],
'development' => ['local', 'testing', 'test', 'dev', 'development', 'ci'],
],
A name on none of the lists is undetermined, never development, so a server that declares a name nobody mapped is not taken for a laptop. Add the name to the right list.
Acknowledging a check
Some findings are decisions. QUEUE_CONNECTION=sync on an installation one person uses is one. Acknowledge such a check by its key, with the reason:
'acknowledged' => [
'queue-connection' => 'One person uses this installation; jobs run in the request on purpose.',
],
An acknowledged check passes, and its summary names the reason and what was found, in the console, in the result laravel-health stores, and in every notification. The decision stays visible instead of turning into silence. An acknowledgement also covers a check that could not look, such as the file checks on a platform that sets the environment without a .env.
Commands
| Command | Runs | Exit codes |
|---|---|---|
php artisan preflight --deploy | the file and configuration checks, and records the deploy | 0 nothing failed, 1 a check failed, 2 a check could not look |
php artisan preflight | every check | the same |
php artisan env:lint [file] | the file checks, against .env or the file named | 0 clean, 1 any finding, warnings included, 2 nothing could be checked |
env:lint is the strict one: two lines with equal values are harmless today and one edit away from a setting that does not apply. --quiet-when-clean prints nothing when nothing was found, for a schedule.
Both commands print values only with --show-values, which --deploy refuses: a deploy keeps its output. What reaches which channel explains why.