brickx/laravel-filament-maintenance
A Filament plugin for managing Laravel maintenance mode from your admin panel. Toggle maintenance on or off, customize the page shown to visitors, and control access without editing config files or running artisan commands.
Verify it still fits your stack first. This package is archived (last release 1.1.2, June 2023) and targets Filament v3 / Laravel 9–10. If you are on Filament v4 or Laravel 11+, stop here — you cannot use it.
composer require brickx/laravel-filament-maintenance
app/Providers/Filament/AdminPanelProvider.php first:
use Brickx\Maintenance\MaintenancePlugin;
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
// ...
->plugin(MaintenancePlugin::make());
}
php artisan optimize:clear, then look for the new Maintenance entry in the Filament sidebar.First use case: the classic pre-deploy flow — an operator clicks the toggle in Filament to put the app into maintenance mode with a custom message and a secret bypass URL, runs php artisan migrate / config:cache on the server, then clicks the toggle again to bring it back up. Confirm it works by opening a private window in incognito without the secret token and checking you get the 503 page.
Treat it as a thin wrapper around php artisan down. Everything the plugin does can be reproduced with the Artisan command, so plan for the day the plugin is removed:
Artisan::call('down', [
'--secret' => 'my-bypass-token', // gives you /my-bypass-token
'--render' => 'errors::503', // custom view
'--retry' => 60, // Retry-After header
]);
Artisan::call('up');
If your team's real need is "toggle from the panel with a nice UI," a 40-line custom Filament Page that calls these two commands is a safer long-term bet than an archived dependency.
Deploy-script integration. The most valuable pattern is calling maintenance mode from CI, not from the UI, because it guarantees the window covers the whole deploy:
php artisan down --secret="$MAINTENANCE_SECRET" --retry=15
php artisan migrate --force
php artisan config:cache && php artisan route:cache && php artisan view:cache
php artisan queue:restart
php artisan up
Keep the panel toggle for unplanned or manual mode; use the script for planned deploys.
Manual mode with the refresh button. 1.1.2 (PR #6) adds a refresh option to the maintenance page so an operator can re-read the current state from disk without reloading the whole panel. This matters when someone else flips the app up/down from the CLI — the Filament page will otherwise show stale state. Use it as the "did my php artisan up actually land?" check before you start debugging a phantom outage.
Wiring "maintenance mode" into application logic. Check state anywhere with the built-in helper rather than shelling out:
if (app()->isDownForMaintenance()) {
// skip outgoing webhooks, pause cron jobs, etc.
}
This is also the cleanest way to unit-test that the toggle works: assert app()->isDownForMaintenance() flips after hitting your Filament page with Livewire's Livewire::test().
Pair with a scheduled window. Laravel 11's php artisan down --with-secret and --allow (IP whitelist) give finer control than most UI plugins do. If you need scheduled maintenance windows, drive them from routes/console.php:
Schedule::call(fn () => Artisan::call('down', ['--retry' => 60]))
->dailyAt('02:00')
->description('nightly maintenance');
Schedule::call(fn () => Artisan::call('up'))->dailyAt('02:30');
Then use the Filament page only as a manual override / status display.
Multi-user safety. If more than one admin can access the panel, add a policy or role gate around the page so a support user can't accidentally take prod down. The plugin's page is a normal Filament Page, so a canView() override or the standard Gate::define('manage-maintenance') + ->visible() chain applies.
"brickx/laravel-filament-maintenance": "1.1.2" (or "1.*") and constrain filament/filament to ^3.0 in composer.json. A composer update that pulls Filament v4 will break the plugin with no maintainer to fix it. Note that 1.1.2 is a patch-level UI tweak — it does not change the support matrix, so nothing here becomes safe to float.php artisan down, which writes storage/framework/down. On a load-balanced multi-server setup, only the node you hit goes down. Use shared storage (EFS/NFS) or a proper edge/load-balancer maintenance page instead./your-secret-token in a tab in advance.down doesn't stop them; jobs will fail against a 503 app if they make HTTP calls. Always run php artisan queue:restart after up, and guard scheduled jobs with app()->isDownForMaintenance().www-data) usually writes storage/framework/down via the panel, but your deploy user (deploy) may then fail to remove it, leaving the app stuck down. Confirm storage/framework/ is group-writable and consistent across users.up feel broken. If you cached config/routes during the window, clear or rebuild them after php artisan up; a stale config:cache will keep the old APP_URL/APP_KEY and produce confusing 500s that look like maintenance-related bugs.--allow availability before relying on it. The IP allow-list option needs a reasonably modern Laravel (8.37+ / 9+). If you're on an older 9.x patch, fall back to the secret token.php artisan down returns 503 — good, monitors alert — but if you use --render with a custom view, make sure you don't accidentally return 200, or your SLO dashboard will go quiet during outages.->view() on the Page class, (b) subclassing the plugin's page and re-registering it, or (c) publishing anything the package ships. Given it's unmaintained, don't expect events/hooks to hook into — verify by reading vendor/brickx/laravel-filament-maintenance/src/. The 1.1.2 refresh option is a UI affordance, not a hook: it re-reads disk state, but there is still no event fired on toggle, so don't build side-effects (Slack pings, audit logs) around it — wrap Artisan::call('down'/'up') yourself if you need those.ls storage/framework/down (does the file exist?), php artisan about (is the app actually in maintenance?), check the reverse proxy/CDN isn't caching the 200 response, and confirm the panel you registered the plugin on is the one you're logged into (multi-panel installs silently do nothing on the wrong panel). Since 1.1.2, hit the refresh control before concluding the toggle failed — the UI may simply be showing a stale snapshot.Artisan::call('down'/'up'), and you lose nothing. A refresh button is trivial to reimplement with $this->redirect(request()->header('Referer')) or a Livewire $refresh action, so 1.1.2 doesn't move the needle on the build-vs-depend call.How can I help you explore Laravel packages today?