Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Maintenance Mode Laravel Package

davidpiesse/nova-maintenance-mode

A Laravel Nova tool for switching your application's maintenance mode on and off from the admin panel. Toggle downtime, set a secret bypass token, and customize the message visitors see—no CLI or config edits needed.

View on GitHub
Deep Wiki
Context7

Getting Started

What it is: a small Nova tool that puts a php artisan down / php artisan up toggle inside the Nova sidebar, so an admin can drop the app into maintenance mode without SSH access. It is a UI wrapper around Laravel's built-in maintenance mode — the actual state lives in the storage/framework/down file, not in a database table.

Before you install:

  • A working Laravel + Nova app (the package's last release is January 2019, so its composer.json targets Nova 1.x/2.x and Laravel 5.x–6.x era APIs — open vendor/davidpiesse/nova-maintenance-mode/composer.json and confirm the constraints against your version before committing to it).
  • Composer auth for the private laravel/nova repository is already configured, since Nova is not on Packagist.
  • Write permission on storage/framework/ for the web user (that is where the down file is created).

Install and register:

composer require davidpiesse/nova-maintenance-mode
// app/Providers/NovaServiceProvider.php
public function tools()
{
    return [
        new \Davidpiesse\NovaMaintenanceMode\MaintenanceMode(),
    ];
}

(Confirm the exact class name in the package README or src/ — Nova tools conventionally live as Vendor\Package\ToolName.)

Where to look first:

  • src/ — the Tool class (extends Laravel\Nova\Tool) and whatever controller/routes back the toggle button. This tells you exactly which Artisan::call() arguments are passed.
  • src/Http/Controllers/ and routes/ — the endpoints the Vue component hits.
  • resources/js/ — the button component, useful if you want to add a confirmation step or a note field.
  • Any config/ file — if one ships, php artisan vendor:publish it so you can set a default secret and bypass list.

First use case: an unplanned incident — a bad deploy or a runaway query — where you need the public site to serve a 503 now and want to do it from your phone. Add your own IP to the bypass list first (see Gotchas), set a secret, then hit the toggle. Verify with curl -I https://yourapp.test that you get a 503 and that your own browser still works via the secret URL.

Implementation Patterns

Gate the tool to admins. Tools support the AuthorizedToSee trait, so you never need a custom middleware:

public function tools()
{
    return array_filter([
        Nova::user()?->isAdmin()
            ? (new MaintenanceMode())->canSee(fn ($request) => $request->user()->isAdmin())
            : null,
    ]);
}

Simpler and often better: only push the tool into the array when config('app.env') === 'production' and the user is an admin, so nobody can accidentally toggle staging from a shared Nova install.

Pair it with a secret and an IP bypass. This is the single most important pattern. Maintenance mode is only safe if you can still get in:

php artisan down --secret="a-long-random-string" --retry=60

Then https://yourapp.test/a-long-random-string sets the bypass cookie. Separately, add trusted IPs (office, VPN, your home IP) to the maintenance middleware's $except array — on Laravel 8+ that is App\Http\Middleware\PreventRequestsDuringMaintenance, on older versions CheckForMaintenanceMode. Nova itself lives behind the same middleware, so without one of these two escape hatches the Nova toggle becomes a one-way trip.

Use it for incidents, not deploys. The most reliable day-to-day pattern is: deploy scripts own the routine down/up around migrations, and the Nova button is reserved for "the site is on fire and I'm not at a laptop." Mixing the two causes confusing states where a deploy's artisan up clears a maintenance window someone opened deliberately.

Reuse the same logic in your own commands. If you want an audit trail, a Slack ping, or a reason string, wrap the toggle in your own Artisan command rather than relying on the tool:

// app/Console/Commands/SiteDown.php
Artisan::call('down', [
    '--secret' => config('app.maintenance_secret'),
    '--retry'  => 120,
]);
Cache::forever('maintenance.reason', $this->argument('reason'));

The Nova tool then becomes a thin "emergency" button, and your command becomes the auditable path. This is the pattern that survives the package being abandoned.

Schedule it for known windows. Backups, index rebuilds, and data imports that lock tables are predictable — schedule down/up in app/Console/Kernel.php around them instead of relying on someone remembering to click a button at 02:00.

Own the 503 page. Publish or create resources/views/errors/503.blade.php with a real message, a support link, and an ETA. On Laravel 8+ you can also point at a custom view with php artisan down --render="errors::maintenance". A bare Laravel 503 during an outage generates support tickets; a branded one with "we're back by 14:00 UTC" does not.

Make health checks cooperate. Emit a Retry-After header (the --retry flag) and configure your load balancer / uptime monitor to treat 503 from the maintenance window as expected rather than paging you — otherwise the on-call alert fires at the exact moment you're already handling an incident.

Gotchas and Tips

You can lock yourself out — permanently, from the browser's perspective. php artisan down makes Nova return 503 too. If no secret is set and your IP isn't bypassed, the toggle cannot be un-toggled from the UI and your only recovery is php artisan up on the server. Confirm the package's controller actually passes a secret before you trust it in production. Treat the first toggle in staging as a test of the escape hatch.

The down file is per-host. In a load-balanced or multi-container setup, storage/framework/down only exists on the machine that ran the command — the other nodes keep serving traffic. Options: shared storage (NFS/EFS) mounted at storage/, run the command on every node via your deploy tooling, or abandon the file for a cache-backed flag read by custom middleware. For ephemeral containers and Laravel Vapor, this package is effectively a no-op — use the platform's own maintenance feature.

Queue workers keep running. artisan down stops HTTP traffic, not the queue. In-flight and newly dispatched jobs continue while the site is "down," which can be exactly wrong during a schema migration. Pair the toggle with php artisan queue:restart (graceful) or queue:work --stop-when-empty in your incident runbook.

Version drift is the real risk here. A January 2019 release predates Laravel 8's middleware rename, the --render and newer down flags, and Nova 3/4/5 entirely. Symptoms: the button returns a 500 because Artisan::call('down', [...]) was passed an option your Laravel version doesn't recognize, or the tool never appears because Nova's asset/route conventions changed. Budget for forking the repo, bumping composer.json constraints, and testing the toggle end-to-end in staging — the package is 200 lines and cheap to own.

Config and route caches can bite. After php artisan config:cache, a published package config with a hard-coded maintenance_secret will be baked in. And any toggle controller that reads env() directly (rather than config()) will silently return null in production. If the secret stops working after a cache clear, that's the first thing to check.

Storage permissions. If the web user can't write to storage/framework/, Artisan::call('down') throws and the Nova UI surfaces an opaque 500. Test with sudo -u www-data php artisan down / up once.

Debugging checklist when the button does nothing:

  1. ls -la storage/framework/down — does the file actually appear/disappear?
  2. Browser dev tools → Network → the POST the Vue component fires; read the JSON error.
  3. storage/logs/laravel.log for a swallowed ProcessFailedException or Artisan exception.
  4. php artisan down --help on your Laravel version — confirm the flags the package passes still exist.
  5. php artisan route:list | grep -i maintenance — is the tool's route registered at all?

Extension points worth using: the Tool's canSee() for authorization, php artisan vendor:publish for the Vue component and any config, a custom errors/503 view, and Nova's Nova::script()/Nova::style() if you want to restyle the button. If the package exposes no config for the secret, the cleanest fork is a five-line edit in the controller that calls Artisan::call('down', ['--secret' => config('app.maintenance_secret')]) — do that before you rely on it in production.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo