Close the site. Not for your team.
A Symfony bundle for maintenance mode with named levels. Visitors get a 503, while the people you choose keep working, by IP, by role or with a signed cookie.
$ composer require mathis-vincent/maintenance-bundle
$ php bin/console maintenance:activate admin --duration=7200
We'll be right back.
Back in 2 hours
Your own Twig templateWho gets through
Each request runs after the security firewall and goes through the active level's rules, in this order.
- Route in
excluded_routesOpen to everyone, no cookie issued - Valid bypass cookieThrough
- IP in
allowed_ipThrough, cookie issued - Role in
allowed_rolesThrough, cookie issued for the role's lifetime - Anyone elseA 503 with the level's template
What's in the box
No database and no cache to set up: the state is a JSON file in var/, read on each request.
- As many levels as you need
- Name them
deploy,db_migrationorpartners_only, each with its own access rules and templates. - Signed bypass cookies
- HMAC-SHA256 with
kernel.secret; secure and httpOnly. A cookie only works for the level it was issued for, so switching level invalidates it. - Reopens by itself
- With
--duration, the site comes back on time even if you've lost access to the server. No cron or worker involved. - Announce it first
maintenance:announcegives you a date to show visitors. It never starts the maintenance.- A banner for those still inside
maintenance_banner()reminds them that maintenance is on and who can reach the page, in English or French, or in your own words.- A 503 that doesn't break
- If your maintenance template fails to render, a plain fallback page still goes out with the right status.
Your look, your words
The bundle imposes no design: the maintenance page and the banner are Twig templates from your project, and each level can have its own.
Visuals
Write the page like any other template, with your own HTML and CSS.
{# templates/maintenance/503.html.twig #}
<link rel="stylesheet" href="/maintenance.css">
<h1>We'll be right back</h1>
{% if maintenance_end() %}
<p>Back on
{{ maintenance_end()|date('d/m/Y H:i') }}</p>
{% endif %}
Translations
English and French are included. Change any text or add a language from your project.
# translations/maintenance.fr.yaml
maintenance:
status:
page:
everyone: 'tout le monde'
nobody: 'personne'
roles: '%roles%'
ips: 'les IP autorisées'
roles_and_ips: '%roles%, les IP autorisées'
Quick start
Four steps, then one command whenever you need it.
-
Install the bundle.
$ composer require mathis-vincent/maintenance-bundle -
Register it in
config/bundles.php.MathisVincent\MaintenanceBundle\MaintenanceBundle::class => ['all' => true], -
Declare your levels in
config/packages/maintenance.yaml.maintenance: role_lifetimes: ROLE_ADMIN: 604800 # bypass cookie lifetime in seconds: 7 days levels: default: # blocks everyone template: 'maintenance/503.html.twig' admin: template: 'maintenance/503.html.twig' banner_template: 'maintenance/banner.html.twig' allowed_ip: ['%env(ADMIN_IP)%'] allowed_roles: ['ROLE_ADMIN'] excluded_routes: ['app_home']The roles in
role_lifetimesandallowed_rolesare your Symfony security roles: the ones your users hold and the ones you define underrole_hierarchyinconfig/packages/security.yaml. The bundle doesn't create any.# config/packages/security.yaml security: role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: ROLE_ADMINThe hierarchy applies: here, a
ROLE_SUPER_ADMINuser also gets throughallowed_roles: ['ROLE_ADMIN'], with theROLE_ADMINlifetime. A role without a lifetime gets 30 days. -
Show the banner in
templates/base.html.twig.<body> {{ maintenance_banner() }} {% block body %}{% endblock %} </body> -
Run it.
Start the generic maintenance (thedefaultlevel)$ php bin/console maintenance:activateOr start a specific level$ php bin/console maintenance:activate adminOr let it reopen by itself after 2 hours$ php bin/console maintenance:activate admin --duration=7200Announce a date to visitors, without starting anything$ php bin/console maintenance:announce "2026-12-01 22:00"Check the current state$ php bin/console maintenance:statusReopen the site$ php bin/console maintenance:deactivate