diff --git a/biscuit/core/settings.py b/biscuit/core/settings.py
index 7d6a5bd4a02f796c0f5c876587b9361be71df5cd..15aa8787a51a3e3ad61404a327339842d0fe3f99 100644
--- a/biscuit/core/settings.py
+++ b/biscuit/core/settings.py
@@ -49,6 +49,7 @@ INSTALLED_APPS = [
     'fa',
     'django_any_js',
     'django_tables2',
+    'maintenance_mode',
     'menu_generator',
     'phonenumber_field',
     'biscuit.core'
@@ -81,6 +82,7 @@ MIDDLEWARE = [
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'easyaudit.middleware.easyaudit.EasyAuditMiddleware',
+    'maintenance_mode.middleware.MaintenanceModeMiddleware',
 ]
 
 ROOT_URLCONF = 'biscuit.core.urls'
@@ -96,6 +98,7 @@ TEMPLATES = [
                 'django.template.context_processors.request',
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
+                'maintenance_mode.context_processors.maintenance_mode',
                 'settings_context_processor.context_processors.settings'
             ],
         },
@@ -233,4 +236,9 @@ ADMINS = _settings.get('admins', [])
 
 TEMPLATE_VISIBLE_SETTINGS = ['ADMINS']
 
+MAINTENANCE_MODE = _settings.get('maintenance.activate', 'None')
+MAINTENANCE_MODE_IGNORE_IP_ADDRESSES = _settings.get('maintenance.ignore_ip_addresses', ())
+MAINTENANCE_MODE_GET_CLIENT_IP_ADDRESS = 'ipware.ip.get_ip'
+MAINTENANCE_MODE_IGNORE_SUPERUSER = True
+
 _settings.populate_obj(sys.modules[__name__])
diff --git a/biscuit/core/urls.py b/biscuit/core/urls.py
index 93b96080c2f4ab87351da1e3e25201da650287fb..6f1da7fffbc4717b0b13db74d5f27911aa5bb61d 100644
--- a/biscuit/core/urls.py
+++ b/biscuit/core/urls.py
@@ -2,7 +2,7 @@ from django.apps import apps
 from django.conf import settings
 from django.conf.urls.static import static
 from django.contrib import admin
-from django.urls import include, path
+from django.urls import include, path, url
 
 from . import views
 
@@ -21,11 +21,13 @@ urlpatterns = [
     path('group/<int:id_>', views.group,
          {'template': 'full'}, name='group_by_id'),
     path('', views.index, name='index'),
+    url(r'^maintenance-mode/', include('maintenance_mode.urls')),
 ]
 
 # Custom error pages
 handler404 = views.error_handler(404)
 handler500 = views.error_handler(500)
+handler503 = views.error_handler(503)
 
 # Serve javascript-common if in development
 if settings.DEBUG:
diff --git a/biscuit/core/views.py b/biscuit/core/views.py
index d661c912de532586b5c3070170b1b1c24324d104..ad982e2cb876bd8ae1510f6eabe0b023c5acc892 100644
--- a/biscuit/core/views.py
+++ b/biscuit/core/views.py
@@ -30,6 +30,9 @@ def error_handler(status: int) -> Callable[..., HttpResponse]:
         elif status == 500:
             context['caption'] = _('Internal server error')
             context['message'] = _('An unexpected error has occurred.')
+        elif status == 503:
+            context['caption'] = _('Maintenance mode')
+            context['message'] = _('The maintenance mode is currently enabled. Please try again later.')
 
         return render(request, 'error.html', context, status=status)
 
diff --git a/pyproject.toml b/pyproject.toml
index 49fa6946718897664981272eb617b704cfcfd67e..42b078e1c3fcee9070ab28711127e01622da0d23 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,6 +37,7 @@ colour = "^0.1.5"
 dynaconf = {version = "^2.0", extras = ["yaml", "toml", "ini"]}
 django-settings-context-processor = "^0.2"
 django-auth-ldap = { version = "^2.0", optional = true }
+django-maintenance-mode = "^0.13.3"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]