Skip to content

Maintenance Mode

Two independent layers, both rendering the same branded maintenance page (see Error Pages — it's dispatched the same way, with "maintenance" as the pseudo status code):

  1. Global env switchAPP_MAINTENANCE. An absolute kill switch: every portal, backoffice included, shows the maintenance page. No CLI access required, just flip the env var and deploy.
  2. Per-portal toggle — controlled from Backoffice → Maintenance. Staff can take any single independent portal down (landing, app, or any custom stub) without touching the others.

Four portals never appear in that per-portal list — maintenance.exempt_portals — for two different reasons:

  • backoffice — staff always need a way in to flip things back.
  • account and auth — supportive portals for app/backoffice/any user-facing portal, not independent destinations of their own. Taking one down in isolation would just strand users mid-login or mid-account-task on whichever portal sent them there.
  • api — not a UI portal at all; there's nothing for a maintenance page to render.

The global switch overrides every exemption above — if APP_MAINTENANCE=true, all four go down too.

                    APP_MAINTENANCE=true?

                 ┌──────────┴──────────┐
                yes                    no
                 │                      │
      EVERY portal down       portal in exempt_portals?
   (backoffice/account/auth/   (backoffice, account,
       api included)              auth, api)

                             ┌──────────┴──────────┐
                            yes                     no
                             │                       │
                    always passes through   per-portal toggle on?

                                           ┌──────────┴──────────┐
                                          yes                    no
                                           │                      │
                                  maintenance page          passes through

Enforcement

App\Http\Middleware\CheckMaintenance is appended globally to the web middleware group in bootstrap/app.php — it covers every current and future portal automatically, no per-route-file edit needed when you scaffold a new one with make:subdomain.

php
// bootstrap/app.php
$middleware->web(append: [
    CheckMaintenance::class,
]);

Config lives in config/maintenance.php:

php
'global' => (bool) env('APP_MAINTENANCE', false),
'exempt_portals' => ['backoffice', 'account', 'auth', 'api'],

Per-portal toggle (Backoffice → Maintenance)

Backed by the portal_settings table (App\Models\PortalSetting) — one row per portal, with maintenance_mode and an optional custom message shown on that portal's maintenance page instead of the generic copy. Reads are cached for 5 minutes (Cache::remember, key portal-setting:{portal}) and busted on every save, so the toggle takes effect on the next request either way.

The backoffice page (resources/views/pages/backoffice/⚡maintenance/) lists every portal in config('multidomain.sub_domains') except those in maintenance.exempt_portals, with a switch and a message field per row.

Native Laravel maintenance mode

php artisan down / php artisan up still work exactly as normal Laravel — this starter kit doesn't touch that mechanism. It's a separate, CLI-only, whole-app switch. For per-portal granularity or an env-driven (no-CLI) toggle, use the system on this page instead.