diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index 13393a49fc56307c64e4d8bce9036bf459dfb873..572e2bbdb484edd28368494364a674635cf15150 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -5,7 +5,6 @@ from datetime import date, datetime, timedelta, time
 from enum import Enum
 from typing import Dict, Optional, Tuple, Union
 
-from constance import config
 from django.core import validators
 from django.core.exceptions import ValidationError
 from django.db import models
@@ -29,7 +28,7 @@ from aleksis.core.mixins import ExtensibleModel
 from aleksis.core.models import Group, Person, DashboardWidget
 
 from aleksis.apps.chronos.util.date import week_weekday_from_date
-from aleksis.core.util.core_helpers import has_person
+from aleksis.core.util.core_helpers import has_person, get_site_preferences
 
 
 class TimetableType(Enum):
@@ -456,7 +455,7 @@ class GroupPropertiesMixin:
     @property
     def groups_to_show(self) -> models.QuerySet:
         groups = self.groups.all()
-        if groups.count() == 1 and groups[0].parent_groups.all() and config.CHRONOS_USE_PARENT_GROUPS:
+        if groups.count() == 1 and groups[0].parent_groups.all() and get_site_preferences()["chronos__use_parent_groups"]:
             return groups[0].parent_groups.all()
         else:
             return groups
@@ -1125,7 +1124,7 @@ class ExtraLesson(ExtensibleModel, GroupPropertiesMixin):
         verbose_name_plural = _("Extra lessons")
 
 
-class GlobalPermissions(ExtensibleModel):
+class ChronosGlobalPermissions(ExtensibleModel):
     class Meta:
         managed = False
         permissions = (
diff --git a/aleksis/apps/chronos/preferences.py b/aleksis/apps/chronos/preferences.py
new file mode 100644
index 0000000000000000000000000000000000000000..69b435a47a964809a02a351f5cb0068dd3c04b33
--- /dev/null
+++ b/aleksis/apps/chronos/preferences.py
@@ -0,0 +1,59 @@
+from django.utils.translation import gettext as _
+
+from dynamic_preferences.preferences import Section
+from dynamic_preferences.types import BooleanPreference, IntegerPreference
+
+from aleksis.core.registries import site_preferences_registry, person_preferences_registry
+
+chronos = Section("chronos", verbose_name=_("Chronos"))
+
+
+@site_preferences_registry.register
+class UseParentGroups(BooleanPreference):
+    section = chronos
+    name = "use_parent_groups"
+    default = False
+    verbose_name = _("Use parent groups in timetable views")
+    help_text = _(
+        "If an lesson or substitution has only one group"
+        " and this group has parent groups,"
+        " show the parent groups instead of the original group."
+    )
+
+
+@person_preferences_registry.register
+class ShortenGroups(BooleanPreference):
+    section = chronos
+    name = "shorten_groups"
+    default = True
+    verbose_name = _("Shorten groups in timetable views")
+    help_text = _(
+        "If there are more groups than the set limit, they will be collapsed."
+    )
+
+
+@site_preferences_registry.register
+class ShortenGroupsLimit(IntegerPreference):
+    section = chronos
+    name = "shorten_groups_limit"
+    default = 4
+    verbose_name = _("Limit of groups for shortening of groups")
+    help_text = _(
+        "If an user activates shortening of groups, they will be collapsed if there are more groups than this limit."
+    )
+
+
+@site_preferences_registry.register
+class SubstitutionsPrintNumberOfDays(IntegerPreference):
+    section = chronos
+    name = "substitutions_print_number_of_days"
+    default = 2
+    verbose_name = _("Number of days shown on substitutions print view")
+
+@site_preferences_registry.register
+class SubstitutionsShowHeaderBox(BooleanPreference):
+    section = chronos
+    name = "substitutions_show_header_box"
+    default = True
+    verbose_name = _("Show header box in substitution views")
+    help_text =  _("The header box shows affected teachers/groups.")
diff --git a/aleksis/apps/chronos/settings.py b/aleksis/apps/chronos/settings.py
deleted file mode 100644
index 559cdca9e7ac2ce981a3730d9073d85973fbd9c7..0000000000000000000000000000000000000000
--- a/aleksis/apps/chronos/settings.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from django.utils.translation import gettext_lazy as _
-
-CONSTANCE_CONFIG = {
-    "CHRONOS_USE_PARENT_GROUPS": (
-        False,
-        _(
-            "If an lesson or substitution has only one group"
-            " and this group has parent groups,"
-            " show the parent groups instead of the original group."
-        ),
-    ),
-    "CHRONOS_SHORTEN_GROUPS": (
-        False,
-        _(
-            "If there are more groups than the limit set in CHRONOS_SHORTEN_GROUPS_LIMIT, add text collapsible."
-        ),
-    ),
-    "CHRONOS_SHORTEN_GROUPS_LIMIT": (
-        4,
-        _(
-            "If there are more groups than this limit and CHRONOS_SHORTEN_GROUPS is enabled, add text collapsible."
-        ),
-    ),
-    "CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER": (
-        2,
-        _("Number of days shown on substitutions print view"),
-    ),
-    "CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX": (
-        True,
-        _("The header box shows affected teachers/groups."),
-    ),
-}
-CONSTANCE_CONFIG_FIELDSETS = {
-    "Chronos settings": (
-        "CHRONOS_USE_PARENT_GROUPS",
-        "CHRONOS_SHORTEN_GROUPS",
-        "CHRONOS_SHORTEN_GROUPS_LIMIT",
-        "CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER",
-        "CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX",
-    ),
-}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/groups.html b/aleksis/apps/chronos/templates/chronos/partials/groups.html
index 26c620bb3f174540cbf4154c283ced6978358ba0..3622cb4166709ddb85d3941b2c205c90e8b0aa94 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/groups.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/groups.html
@@ -1,4 +1,4 @@
-{% if groups.count == 1 and groups.0.parent_groups.all and config.CHRONOS_USE_PARENT_GROUPS %}
+{% if groups.count == 1 and groups.0.parent_groups.all and request.site.preferences.chronos__use_parent_groups %}
   {% include "chronos/partials/groups_part.html" with groups=groups.0.parent_groups.all %}
 {% else %}
   {% include "chronos/partials/groups_part.html" with groups=groups %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/groups_part.html b/aleksis/apps/chronos/templates/chronos/partials/groups_part.html
index fea197c9bca0fcd87e1e4b36b065a70e1076917b..0cae5bceb747503b92434baa434ed32fb9589d64 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/groups_part.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/groups_part.html
@@ -1,4 +1,4 @@
-{% if groups.count > config.CHRONOS_SHORTEN_GROUPS_LIMIT and config.CHRONOS_SHORTEN_GROUPS %}
+{% if groups.count > request.site.preferences.chronos__shorten_groups_limit and request.user.preferences.chronos__shorten_groups %}
   {% include "components/text_collapsible.html" with template="chronos/partials/group.html" qs=groups %}
 {% else %}
   {% for group in groups %}
diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py
index ef128e754e4ebb32e7ed7ef23bf42cd1b4b924fd..b0e593f57b59d172db0c1f4610fcec8867871414 100644
--- a/aleksis/apps/chronos/views.py
+++ b/aleksis/apps/chronos/views.py
@@ -2,7 +2,6 @@ from collections import OrderedDict
 from datetime import date, datetime, timedelta
 from typing import Optional, Tuple
 
-from constance import config
 from django.contrib.auth.decorators import login_required
 from django.db.models import Count
 from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
@@ -21,7 +20,7 @@ from .tables import LessonsTable
 from .util.build import build_timetable, build_substitutions_list, build_weekdays
 from .util.js import date_unix
 from .util.date import CalendarWeek, get_weeks_for_year
-from aleksis.core.util.core_helpers import has_person
+from aleksis.core.util.core_helpers import has_person, get_site_preferences
 
 
 @permission_required("chronos.view_timetable_overview")
@@ -293,7 +292,7 @@ def substitutions(
     else:
         wanted_day = TimePeriod.get_next_relevant_day(timezone.now().date(), datetime.now().time())
 
-    day_number = config.CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER
+    day_number = get_site_preferences()["chronos__substitutions_print_number_of_days"]
     day_contexts = {}
 
     if is_print:
@@ -310,7 +309,7 @@ def substitutions(
 
         day_contexts[day]["announcements"] = Announcement.for_timetables().on_date(day).filter(show_in_timetables=True)
 
-        if config.CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX:
+        if get_site_preferences()["chronos__substitutions_show_header_box"]:
             subs = LessonSubstitution.objects.on_day(day).order_by("lesson_period__lesson__groups",
                                                                    "lesson_period__period")
             absences = Absence.objects.on_day(day)