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

Maintenance Bundle Laravel Package

toshy/maintenance-bundle

A PHP maintenance-mode bundle that lets you temporarily disable your application, display a maintenance page, and configure bypass access for admins or authorized users during deployments and updates.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framing. This is not a Laravel package. It's a Symfony 2 bundle (Toshy\... fork of LexikMaintenanceBundle): no service provider, no composer auto-discovery, no facades, and it targets Symfony\Component\HttpKernel\*, app/console, and app/config/config.yml. composer require inside a Laravel app is a dead end. The 0 stars / 0 dependents is a fair signal too — treat this as a reference implementation of a maintenance-mode pattern, not a dependency.

Where to look first (in this order):

  1. README.md — the two commands and the config block are the entire surface area.
  2. EventListener/MaintenanceListener.php — the whole feature lives here: on kernel.request, is there a lock, is it expired, does the client IP bypass, render 503.
  3. Command/Lock/UnlockCommand.php — shows the state is just a file (or cache/db entry).
  4. DependencyInjection/Configuration.php — the authoritative list of config keys (authorized.ips, driver, ttl, response.code, response.template).

First use case, in the host Symfony app: register the bundle in AppKernel, add a lexik_maintenance block with authorized.ips containing your office NAT and your load balancer's health-check IPs, php app/console lexik:maintenance:lock --ttl=900, confirm you still get 200 from an allowlisted IP and a clean 503 from a phone on cellular, then unlock. That five-minute loop tells you everything about whether the package fits your infra.

First use case, in Laravel — because that's what you actually ship — is the built-in equivalent: php artisan down --secret="staging-token" --render="errors::503" and php artisan up, backed by PreventRequestsDuringMaintenance. Same concept, one fewer bundle, and bypass via secret cookie instead of IP.

Implementation Patterns

Lock/unlock as a deploy step. The bundle is designed for a boolean lifecycle, not a scheduler. The pattern that works is wrapping it in your release script: lock → pull/migrate/warm cache → unlock, with --ttl as the dead-man's-switch so a botched deploy doesn't leave the site dark forever. In Laravel the exact same shape applies with artisan down / artisan up inside your Envoyer/Deployer hooks.

Static IP allowlist. authorized.ips is the only bypass mechanism. Good for: office egress IP, VPN range, uptime monitor, LB health-check node. Bad for: anyone on mobile, any CI runner on a dynamic cloud IP, and any client who wants to "just take a look." Because it lives in config, it requires a deploy to change — that's a real operational cost during an incident.

Dependency-free 503 template. Point response.template at a Twig template with inline CSS and zero queries. The failure mode you're guarding against is usually a database or asset pipeline; a maintenance page that itself needs the DB is worthless. Keep it under a few KB and inline it.

Multi-node is the decision point. The default file driver writes under %kernel.cache_dir%, which is per-host. On one box it's perfect. On five app servers, locking one does nothing for the other four. Either run the lock command on every node from your orchestrator, or use the cache/database driver against shared storage. Pick this before you build anything on top.

Translate to Laravel as middleware, not as a package. If artisan down is too blunt (per-tenant maintenance, per-plan windows), the reusable idea from this bundle is the DriverInterface (isLocked() / getMessage()) plus a kernel.request listener that short-circuits with a 503. That's a ~40-line MaintenanceMiddleware + a small MaintenanceManager service in Laravel, and you keep the IP/secret bypass logic under your own control.

Health-check carve-outs. Whatever you do, exclude /up, /health, /status from maintenance — either allowlist the LB's IPs or put those paths in $except. Otherwise the LB sees 503s, marks every node unhealthy, and your "nice maintenance page" is replaced by a raw 502 from the edge.

Gotchas and Tips

The lock file lives in the cache directory. A cache:clear, a rm -rf var/cache, or a deploy that creates a fresh release directory silently unlocks your site — or worse, leaves the old release locked while the new one serves traffic unlocked. If you depend on this, move driver.file.path somewhere stable outside the cache dir.

TTL is evaluated at request time, not by a cron. An expired lock file still sits on disk forever; the listener just stops honoring it. So don't monitor "does the lock file exist" — monitor the HTTP status code of a real request.

IP matching breaks behind a proxy. The listener uses the client IP, which is the proxy's IP unless trusted_proxies (and the right X-Forwarded-For handling) is configured. Two failure modes: allowlist matches nobody and everyone gets 503, or you allowlist the LB and every single request bypasses maintenance. Get trusted proxies correct before you trust this feature at all.

Symfony 2 is EOL. This is PHP 5.4-era code with Twig 1.x and app/console. It won't install on a modern stack without a fork, and with 0 stars/0 dependents there's no upstream to fix it. Read it, don't depend on it.

Bypass is IP-only. There's no secret URL, no cookie, no header bypass in the box. Laravel's --secret already does this better; don't reinvent it with an IP allowlist that ages badly the first time someone works from home.

Debugging a "it's locked but I get 200" report: (1) is the listener actually registered and enabled, (2) does the file exist at the resolved path (dump the container parameter, don't guess), (3) has the TTL elapsed, (4) did your IP match the allowlist. A "locked but 503 with a stack trace" is almost always the maintenance template itself throwing — strip it to plain HTML and bisect from there.

Keep 503, and send Retry-After. 503 is correct because it's explicitly retryable; crawlers will re-index. 403 or 404 for maintenance is how you get pages permanently dropped from search results.

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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment