Installation
composer require lexik/maintenance-bundle
Add to config/bundles.php:
return [
// ...
Lexik\MaintenanceBundle\LexikMaintenanceBundle::class => ['all' => true],
];
Configure Storage
Edit config/packages/lexik_maintenance.yaml to choose a storage backend (file, memcache, or database):
lexik_maintenance:
storage: file # or 'memcache', 'database'
file_path: '%kernel.project_dir%/var/maintenance' # Required for file storage
memcache: ~ # Configure if using memcache
database: ~ # Configure if using database
First Use Case Enable maintenance mode:
php bin/console lexik:maintenance:enable
Disable it:
php bin/console lexik:maintenance:disable
config/packages/lexik_maintenance.yamlbin/console list lexik:maintenance (for available commands)templates/lexik_maintenance/maintenance.html.twig (customize the 503 page)Deploy Workflow
php bin/console lexik:maintenance:enable --ip=127.0.0.1 --ip=192.168.1.100
git pull, composer install).php bin/console lexik:maintenance:disable
Scheduled Maintenance Use a cron job or task scheduler to enable maintenance during off-peak hours:
0 3 * * * php /path/to/bin/console lexik:maintenance:enable --ip=your-office-ip
0 5 * * * php /path/to/bin/console lexik:maintenance:disable
IP Whitelisting Allow specific IPs (e.g., your team or CDN) while blocking others:
lexik_maintenance:
allowed_ips: ['192.168.1.100', '203.0.113.5', 'CDN_IP_RANGE']
Custom 503 Page
Override the default Twig template (maintenance.html.twig) to match your brand:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Under Maintenance</h1>
<p>We'll be back soon!</p>
{% endblock %}
Database Storage For shared hosting (no file/memcache access), use database storage:
lexik_maintenance:
storage: database
database:
table: maintenance_flags
Run migrations:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
Symfony Events Trigger maintenance mode dynamically via events (e.g., after a deploy):
use Lexik\MaintenanceBundle\Event\MaintenanceEvent;
// In a service or controller
$this->eventDispatcher->dispatch(
new MaintenanceEvent(true, ['192.168.1.100']),
MaintenanceEvents::ENABLE
);
Environment-Specific Config
Use %kernel.environment% to enable maintenance only in prod:
# config/packages/lexik_maintenance.yaml
lexik_maintenance:
enabled: '%env(bool:MAINTENANCE_ENABLED)%'
File Permissions
If using file storage, ensure the var/maintenance file is writable:
chmod 644 var/maintenance
file_path in config and permissions.Caching Issues Clear caches after enabling/disabling maintenance:
php bin/console cache:clear
lexik:maintenance:clear-cache.IP Whitelisting Conflicts
If using allowed_ips in config and passing --ip via CLI, the CLI flag takes precedence.
Database Storage Quirks
maintenance_flags table exists (run migrations).Memcache/Memcached Misconfiguration
memcache config matches your server setup:
lexik_maintenance:
storage: memcache
memcache:
servers: ['127.0.0.1:11211']
php bin/console debug:container lexik_maintenance.memcache).Check Status Run this command to verify maintenance mode:
php bin/console lexik:maintenance:status
Output:
Maintenance mode is enabled.
Allowed IPs: [192.168.1.100, 127.0.0.1]
Log Storage Operations Enable debug mode to log storage operations:
lexik_maintenance:
debug: true
Check logs in var/log/dev.log.
Custom Storage
Implement Lexik\MaintenanceBundle\Storage\StorageInterface for custom backends (e.g., Redis):
class RedisStorage implements StorageInterface {
public function isEnabled(): bool { /* ... */ }
public function enable(array $ips): void { /* ... */ }
public function disable(): void { /* ... */ }
}
Register it in services.yaml:
services:
Lexik\MaintenanceBundle\Storage\StorageInterface: '@app.redis_storage'
Dynamic IP Whitelisting
Override the AllowedIpChecker to fetch IPs dynamically (e.g., from an API):
use Lexik\MaintenanceBundle\Checker\AllowedIpCheckerInterface;
class ApiAllowedIpChecker implements AllowedIpCheckerInterface {
public function isAllowed(string $ip): bool {
// Call external API to validate IP
return $api->isIpAllowed($ip);
}
}
Multi-Template Support Use a template resolver to switch between maintenance pages (e.g., per locale):
use Lexik\MaintenanceBundle\Twig\MaintenanceExtension;
class CustomMaintenanceExtension extends MaintenanceExtension {
public function getTemplate(string $locale): string {
return sprintf('lexik_maintenance/maintenance_%s.html.twig', $locale);
}
}
Environment Variables
Use %env() for dynamic config (e.g., allowed_ips):
lexik_maintenance:
allowed_ips: '%env(string:MAINTENANCE_ALLOWED_IPS)%'
Set in .env:
MAINTENANCE_ALLOWED_IPS="192.168.1.100,127.0.0.1"
Default Template Location The bundle looks for templates in:
templates/lexik_maintenance/maintenance.html.twig (project)vendor/lexik/maintenance-bundle/Resources/views/maintenance.html.twig (fallback)Storage Priority
The bundle checks storage backends in this order: database > memcache > file. Ensure only one is configured.
How can I help you explore Laravel packages today?