diff --git a/src/strass/live_settings/__init__.py b/src/strass/live_settings/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f496f40992ff686bda9681882f3e820436a5aae
--- /dev/null
+++ b/src/strass/live_settings/__init__.py
@@ -0,0 +1,27 @@
+from django.core.cache import cache
+from django.db import OperationalError, ProgrammingError
+
+
+class LiveSettings(object):
+    def __setattr__(self, key, value, *args, **kwargs):
+        try:
+            from live_settings.models import LiveSettings
+
+            LiveSettings.objects.update_or_create(key=key, defaults=dict(value=value))
+        except (ProgrammingError, OperationalError):
+            return super().__setattr__(key, value)
+
+    def __getattribute__(self, key):
+        live_settings_dict = cache.get("live_settings_dict")
+        if live_settings_dict is None:
+            try:
+                from live_settings.models import LiveSettings
+
+                live_settings_dict = dict(LiveSettings.objects.values_list("key", "value"))
+                cache.set("live_settings_dict", live_settings_dict)
+            except (ProgrammingError, OperationalError):
+                return super().__getattribute__(key)
+        return live_settings_dict.get(key, None)
+
+
+live_settings = LiveSettings()
diff --git a/src/strass/live_settings/admin.py b/src/strass/live_settings/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..31c733caf05f3f49bceb70104dab46dd989deab8
--- /dev/null
+++ b/src/strass/live_settings/admin.py
@@ -0,0 +1,11 @@
+from django.contrib import admin
+
+from live_settings import models
+
+
+class LiveSettingsAdmin(admin.ModelAdmin):
+    list_display = ("key", "last_edition_date")
+    readonly_fields = ["last_edition_date"]
+
+
+admin.site.register(models.LiveSettings, LiveSettingsAdmin)
diff --git a/src/strass/live_settings/context_processors.py b/src/strass/live_settings/context_processors.py
new file mode 100644
index 0000000000000000000000000000000000000000..9fc38cc3fe1c0f34998ba57df5194cbbe2655749
--- /dev/null
+++ b/src/strass/live_settings/context_processors.py
@@ -0,0 +1,5 @@
+from live_settings import live_settings
+
+
+def processors(request):
+    return dict(live_settings=live_settings)
diff --git a/src/strass/live_settings/forms.py b/src/strass/live_settings/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..c636c9a796c9956a2122bb8dc100043cc0069844
--- /dev/null
+++ b/src/strass/live_settings/forms.py
@@ -0,0 +1,12 @@
+from django import forms
+
+from live_settings import models
+
+
+class LiveSettingsForm(forms.ModelForm):
+    next = forms.CharField(widget=forms.HiddenInput(), required=True)
+
+    class Meta:
+        model = models.LiveSettings
+        fields = ("value",)
+        widgets = {"value": forms.HiddenInput()}
diff --git a/src/strass/live_settings/migrations/0001_initial.py b/src/strass/live_settings/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..cabe4e90c83a589304b6d4a795de420cfbfd3a7c
--- /dev/null
+++ b/src/strass/live_settings/migrations/0001_initial.py
@@ -0,0 +1,24 @@
+# Generated by Django 2.2.5 on 2019-11-20 10:20
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='LiveSettings',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('key', models.CharField(max_length=256, unique=True, validators=[django.core.validators.RegexValidator(code='invalid_live_settings_key', message='LiveSettings key must must be Alphanumeric and start with a letter: <code>^[a-zA-Z][\\w]*$</code>.', regex='^[a-zA-Z][\\w]*$')])),
+                ('value', models.TextField(blank=True, null=True)),
+                ('last_edition_date', models.DateTimeField()),
+            ],
+        ),
+    ]
diff --git a/src/strass/live_settings/migrations/0002_create_group_settings_changer.py b/src/strass/live_settings/migrations/0002_create_group_settings_changer.py
new file mode 100644
index 0000000000000000000000000000000000000000..efcdf440c9c75a6b329f2caf6f22abebc6a72982
--- /dev/null
+++ b/src/strass/live_settings/migrations/0002_create_group_settings_changer.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.contrib.auth import get_permission_codename
+from django.contrib.auth.models import Permission, Group
+from django.contrib.contenttypes.models import ContentType
+from django.db import migrations
+
+
+def migration_code(apps, schema_editor):
+    # inspired by django.contrib.auth.management.create_permissions
+    group, _ = Group.objects.get_or_create(name="LiveSettingEditor")
+
+    for klass in [
+        apps.get_model("live_settings", "LiveSettings"),
+    ]:
+        ct = ContentType.objects.get_for_model(klass)
+        opts = klass._meta
+
+        for action in klass._meta.default_permissions:
+            perm, _ = Permission.objects.get_or_create(
+                codename=get_permission_codename(action, opts),
+                name='Can %s %s' % (action, opts.verbose_name_raw),
+                content_type=ct)
+            group.permissions.add(perm)
+
+
+def reverse_code(apps, schema_editor):
+    Group.objects.filter(name="LiveSettingEditor").delete()
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('live_settings', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.RunPython(migration_code, reverse_code=reverse_code),
+    ]
diff --git a/src/strass/live_settings/migrations/0003_auto_20200311_1037.py b/src/strass/live_settings/migrations/0003_auto_20200311_1037.py
new file mode 100644
index 0000000000000000000000000000000000000000..498cafe9b0b7b96e8db499fff04b4b38c307d4a1
--- /dev/null
+++ b/src/strass/live_settings/migrations/0003_auto_20200311_1037.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.2.5 on 2020-03-11 09:37
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('live_settings', '0002_create_group_settings_changer'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='livesettings',
+            options={'verbose_name': 'Live setting', 'verbose_name_plural': 'Live settings'},
+        ),
+    ]
diff --git a/src/strass/live_settings/migrations/__init__.py b/src/strass/live_settings/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/strass/live_settings/models.py b/src/strass/live_settings/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..bec35c8903b6424cb1536c9d0b083f5d1f70fc65
--- /dev/null
+++ b/src/strass/live_settings/models.py
@@ -0,0 +1,45 @@
+from __future__ import unicode_literals
+
+from django.core.cache import cache
+from django.core.validators import RegexValidator
+from django.db import models
+from django.db.models.signals import post_save, post_delete
+from django.dispatch import receiver
+from django.utils import timezone
+from django.utils.safestring import mark_safe
+
+
+class LiveSettings(models.Model):
+    class Meta:
+        verbose_name = "Live setting"
+        verbose_name_plural = "Live settings"
+
+    regex = "^[a-zA-Z][\\w]*$"
+    key = models.CharField(
+        max_length=256,
+        unique=True,
+        validators=(
+            RegexValidator(
+                regex=regex,
+                message=mark_safe(
+                    "LiveSettings key must must be Alphanumeric and start with a letter: " "<code>%s</code>." % regex
+                ),
+                code="invalid_live_settings_key",
+            ),
+        ),
+    )
+    value = models.TextField(
+        blank=True,
+        null=True,
+    )
+    last_edition_date = models.DateTimeField()
+
+    def save(self, *args, **kwargs):
+        self.last_edition_date = timezone.now()
+        super().save(*args, **kwargs)
+
+
+@receiver(post_save, sender=LiveSettings)
+@receiver(post_delete, sender=LiveSettings)
+def flush_live_settings_in_cache(*args, **kwargs):
+    cache.delete("live_settings_dict")
diff --git a/src/strass/live_settings/templates/admin/index.html b/src/strass/live_settings/templates/admin/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..8517f906f3da53f1eb7c2a3a76143ae720fa0b3a
--- /dev/null
+++ b/src/strass/live_settings/templates/admin/index.html
@@ -0,0 +1,57 @@
+{% extends "admin/index.html" %}
+{% load i18n static basetheme_bootstrap %}
+
+{% block extrastyle %}
+{{ block.super }}
+<style>
+#content-related{
+    background: none;
+}
+#content-related .module{
+    background: #f8f8f8;
+}
+#site-wide-commands form{
+    margin:15px 7px;
+}
+</style>
+{% endblock %}
+
+{% block sidebar %}
+<div id="content-related">
+    {% if perms.live_settings.change_livesettings %}
+    <div class="module" id="site-wide-commands">
+        <h2>{% trans 'Site wide settings' %}</h2>
+        {%include_if_exists "live_settings/sitewidecommands.html" %}
+    </div>
+    {%endif%}
+
+    <div class="module" id="recent-actions-module">
+        <h2>{% trans 'Recent actions' %}</h2>
+        <h3>{% trans 'My actions' %}</h3>
+            {% load log %}
+            {% get_admin_log 10 as admin_log for_user user %}
+            {% if not admin_log %}
+            <p>{% trans 'None available' %}</p>
+            {% else %}
+            <ul class="actionlist">
+            {% for entry in admin_log %}
+            <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
+                {% if entry.is_deletion or not entry.get_admin_url %}
+                    {{ entry.object_repr }}
+                {% else %}
+                    <a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
+                {% endif %}
+                <br>
+                {% if entry.content_type %}
+                    <span class="mini quiet">{% filter capfirst %}{{ entry.content_type }}{% endfilter %}</span>
+                {% else %}
+                    <span class="mini quiet">{% trans 'Unknown content' %}</span>
+                {% endif %}
+            </li>
+            {% endfor %}
+            </ul>
+            {% endif %}
+        <hr/>
+    </div>
+</div>
+{% endblock %}
\ No newline at end of file
diff --git a/src/strass/live_settings/tests.py b/src/strass/live_settings/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..23ae0c55111275222d707473d987307197560dd2
--- /dev/null
+++ b/src/strass/live_settings/tests.py
@@ -0,0 +1,76 @@
+import logging
+from unittest import TestCase
+
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Permission
+from django.contrib.contenttypes.models import ContentType
+from django.core import management
+from django.core.cache import cache
+from django.test import TestCase as DjangoTestCase
+from django.urls import reverse
+
+from live_settings import live_settings, models
+
+logger = logging.getLogger(__name__)
+
+
+class LiveSettingsTestCase(DjangoTestCase):
+    def setUp(self) -> None:
+        super().setUp()
+        ################################################################################
+        self.user = get_user_model().objects.create(
+            username="root",
+        )
+
+    def test_get_value(self):
+        self.assertIsNone(live_settings.toto)
+        models.LiveSettings.objects.create(key="toto", value="tata")
+        self.assertIsNotNone(live_settings.toto)
+        self.assertEqual(live_settings.toto, "tata")
+
+    def test_get_set(self):
+        live_settings.tralala = 1
+        self.assertEqual(str(live_settings.tralala), "1")
+
+    def test_view_works(self):
+        form_data = dict(value="titi", next="/")
+        url = reverse('live_settings:update', args=["toto"])
+
+        response = self.client.post(url, form_data)
+        self.assertEqual(response.status_code, 302)
+        self.assertNotEqual(live_settings.toto, form_data["value"])
+
+        self.client.force_login(self.user)
+
+        response = self.client.post(url, form_data)
+        self.assertEqual(response.status_code, 302)
+        self.assertNotEqual(live_settings.toto, form_data["value"])
+
+        change = Permission.objects.get(
+            content_type=ContentType.objects.get_for_model(models.LiveSettings), codename__startswith="change"
+        )
+        self.user.user_permissions.add(change)
+
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 403)
+        self.assertNotEqual(live_settings.toto, form_data["value"])
+
+        response = self.client.post(url, dict(value=form_data["value"]))
+        self.assertEqual(response.status_code, 400)
+        self.assertNotEqual(live_settings.toto, form_data["value"])
+
+        response = self.client.post(url, form_data)
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(live_settings.toto, form_data["value"])
+
+
+class LiveSettingsNoDBTestCase(TestCase):
+    def test_get_value(self):
+        management.call_command("migrate", "live_settings", "zero", no_input=True, skip_checks=True)
+        cache.delete("live_settings_dict")
+        try:
+            ex = live_settings.tralala
+        except Exception as e:
+            ex = e
+        assert isinstance(ex, AttributeError)
+        live_settings.tralala = 1
diff --git a/src/strass/live_settings/urls.py b/src/strass/live_settings/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..b638d2d812328bf510e7dbd7f8b8e563c6bcf8c0
--- /dev/null
+++ b/src/strass/live_settings/urls.py
@@ -0,0 +1,6 @@
+from django.conf.urls import url
+
+from live_settings import views
+
+app_name = "live_settings"
+urlpatterns = [url(r"^update/(?P<slug>[a-zA-Z][\w]*)/$", views.change_value, name="update")]
diff --git a/src/strass/live_settings/views.py b/src/strass/live_settings/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a20f019516b6b8080feb0ff1bbf4241fbe82f68
--- /dev/null
+++ b/src/strass/live_settings/views.py
@@ -0,0 +1,20 @@
+from django.contrib.auth.decorators import permission_required
+from django.http import HttpResponseForbidden, HttpResponseBadRequest
+from django.shortcuts import redirect
+
+from live_settings import forms
+from live_settings import live_settings
+
+
+@permission_required("live_settings.change_livesettings")
+def change_value(request, slug):
+    if request.method != "POST":
+        return HttpResponseForbidden()
+
+    form = forms.LiveSettingsForm(data=request.POST)
+    if not form.is_valid():
+        return HttpResponseBadRequest()
+
+    setattr(live_settings, slug, form.cleaned_data["value"])
+
+    return redirect(form.cleaned_data["next"])
diff --git a/src/strass/strass/settings.py b/src/strass/strass/settings.py
index 41286e21bfd71abe74028a46363cd298de1ddf47..187741a6b8442b939875fc77b9198cea56851d35 100644
--- a/src/strass/strass/settings.py
+++ b/src/strass/strass/settings.py
@@ -38,6 +38,7 @@ DEFAULT_DOMAIN = 'https://{}'.format(ALLOWED_HOSTS[0])
 # Application definition
 
 INSTALLED_APPS = [
+    'live_settings',
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
@@ -76,6 +77,7 @@ TEMPLATES = [
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
                 'basetheme_bootstrap.context_processors.processors',
+                "live_settings.context_processors.processors",
             ],
         },
     },
diff --git a/src/strass/strass/urls.py b/src/strass/strass/urls.py
index eae1fb1ec91b5dbd3cae9dca75f2e93fcc0ddd8c..57a694c8af30987025646557f13bf6eb5f4a75a2 100644
--- a/src/strass/strass/urls.py
+++ b/src/strass/strass/urls.py
@@ -18,6 +18,7 @@ from django.urls import path, include
 
 urlpatterns = [
     path('', include('basetheme_bootstrap.urls')),
+    path('live_settings/', include("live_settings.urls")),
     path('', include('strass_app.urls')),
     path('admin/', admin.site.urls),
 ]