diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py
index 1647161460720845a42f4b5896622048b32c07ae..aaee34bc1439fb045626ec07768f7b9555c67ef7 100644
--- a/aleksis/core/apps.py
+++ b/aleksis/core/apps.py
@@ -1,5 +1,6 @@
 from django.apps import AppConfig, apps
-from django.db.models.signals import post_save
+
+from constance.signals import config_updated
 
 from .signals import clean_scss
 
@@ -10,4 +11,4 @@ class CoreConfig(AppConfig):
 
     def ready(self) -> None:
         clean_scss()
-        post_save.connect(clean_scss, sender=apps.get_model("dbsettings", "Setting"))
+        config_updated.connect(clean_scss)
diff --git a/aleksis/core/db_settings.py b/aleksis/core/db_settings.py
deleted file mode 100644
index 7e5c8229396c2c273d4e991c4c94f072a26dd4c5..0000000000000000000000000000000000000000
--- a/aleksis/core/db_settings.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from django.utils.translation import ugettext_lazy as _
-
-import dbsettings
-
-
-class ThemeSettings(dbsettings.Group):
-    colour_primary = dbsettings.StringValue(_("Primary colour"), default="#007bff")
-    colour_secondary = dbsettings.StringValue(_("Secondary colour"), default="#007bff")
-
-
-class MailSettings(dbsettings.Group):
-    mail_out_name = dbsettings.StringValue(_("Mail out name"), default="AlekSIS", required=False)
-    mail_out = dbsettings.StringValue(_("Mail out address"), default="no-reply@aleksis.org")
-
-
-class FooterSettings(dbsettings.Group):
-    privacy_url = dbsettings.StringValue(_("Link to privacy policy"), default="")
-    impress_url = dbsettings.StringValue(_("Link to impress"), default="")
-
-
-theme_settings = ThemeSettings(_("Global theme settings"))
-mail_settings = MailSettings(_("Mail settings"))
-footer_settings = FooterSettings(_("Footer links"))
-
-db_settings = {
-    "theme": theme_settings,
-    "mail": mail_settings,
-    "footer": footer_settings
-}
diff --git a/aleksis/core/mailer.py b/aleksis/core/mailer.py
index 8ae90edb9f3397b4e401e668dc2cc2fbe14d870b..e4dc6b4479ccd9e1ca5644108c3e0be7ed6ca4d5 100644
--- a/aleksis/core/mailer.py
+++ b/aleksis/core/mailer.py
@@ -1,10 +1,11 @@
 from django.core.mail import send_mail
 from django.template.loader import render_to_string
 
-from aleksis.core.db_settings import mail_settings
+from constance import config
 
-mail_out = "{} <{}>".format(mail_settings.mail_out_name,
-                            mail_settings.mail_out) if mail_settings.mail_out_name != "" else mail_settings.mail_out
+
+mail_out = "{} <{}>".format(config.MAIL_OUT_NAME,
+                            config.MAIL_OUT) if config.MAIL_OUT_NAME else config.MAIL_OUT
 
 
 def send_mail_with_template(title, receivers, plain_template, html_template, context={}, mail_out=mail_out):
diff --git a/aleksis/core/processors.py b/aleksis/core/processors.py
deleted file mode 100644
index fcfe15407d10b354dd01a43f8d9921a67ccef89a..0000000000000000000000000000000000000000
--- a/aleksis/core/processors.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from aleksis.core.db_settings import db_settings
-
-
-def db_settings_processor(request):
-    return {"DB_SETTINGS": db_settings}
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index a27912a7a99fe6b4f4fd4fa50fd592a11afe6ce5..3af57c9e8a635f0d8d010b96b15f11c48f89743c 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -53,7 +53,7 @@ INSTALLED_APPS = [
     "settings_context_processor",
     "sass_processor",
     "easyaudit",
-    "dbsettings",
+    "constance",
     "django_any_js",
     "django_yarnpkg",
     "django_tables2",
@@ -119,7 +119,7 @@ TEMPLATES = [
                 "django.contrib.messages.context_processors.messages",
                 "maintenance_mode.context_processors.maintenance_mode",
                 "settings_context_processor.context_processors.settings",
-                "aleksis.core.processors.db_settings_processor"
+                "constance.context_processors.config"
             ],
         },
     },
@@ -250,7 +250,7 @@ ANY_JS = {
 SASS_PROCESSOR_AUTO_INCLUDE = False
 SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
     "get-colour": "aleksis.core.util.sass_helpers.get_colour",
-    "get-theme-setting": "aleksis.core.util.sass_helpers.get_theme_setting",
+    "get-setting": "aleksis.core.util.sass_helpers.get_setting",
 }
 SASS_PROCESSOR_INCLUDE_DIRS = [_settings.get("materialize.sass_path", JS_ROOT + "/materialize-css/sass/"), STATIC_ROOT]
 
@@ -271,6 +271,24 @@ if _settings.get("mail.server.host", None):
 
 TEMPLATE_VISIBLE_SETTINGS = ["ADMINS", "DEBUG"]
 
+CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
+CONSTANCE_ADDITIONAL_FIELDS = {
+    "image_field": ["django.forms.ImageField", {}]
+}
+CONSTANCE_CONFIG = {
+    "COLOUR_PRIMARY": ("#007bff", _("Primary colour")),
+    "COLOUR_SECONDARY": ("#007bff", _("Secondary colour")),
+    "MAIL_OUT_NAME": ("AlekSIS", _("Mail out name")),
+    "MAIL_OUT": ("aleksis@example.com", _("Mail out address")),
+    "PRIVACY_URL": ("", _("Link to privacy policy")),
+    "IMPRINT_URL": ("", _("Link to imprint")),
+}
+CONSTANCE_CONFIG_FIELDSETS = {
+    "Theme settings": ("COLOUR_PRIMARY", "COLOUR_SECONDARY"),
+    "Mail settings": ("MAIL_OUT_NAME", "MAIL_OUT"),
+    "Footer settings": ("PRIVACY_URL", "IMPRINT_URL"),
+}
+
 MAINTENANCE_MODE = _settings.get("maintenance.enabled", None)
 MAINTENANCE_MODE_IGNORE_IP_ADDRESSES = _settings.get(
     "maintenance.ignore_ips", _settings.get("maintenance.internal_ips", [])
diff --git a/aleksis/core/static/theme.scss b/aleksis/core/static/theme.scss
index 6cb2c96081bad9721be4e4a6bee006c698cf4ea2..18b344169fe115d9175d8ac76df80d7dc9c178ae 100644
--- a/aleksis/core/static/theme.scss
+++ b/aleksis/core/static/theme.scss
@@ -34,11 +34,11 @@
 // 1. Colors
 // ==========================================================================
 
-$primary-color: adjust-color(get-colour(get-theme-setting(colour_primary)), $alpha: 1);
+$primary-color: adjust-color(get-colour(get-setting(COLOUR_PRIMARY)), $alpha: 1);
 $primary-color-light: lighten($primary-color, 15%) !default;
 $primary-color-dark: darken($primary-color, 15%) !default;
 
-$secondary-color: adjust-color(get-colour(get-theme-setting(colour_secondary)), $alpha: 1);
+$secondary-color: adjust-color(get-colour(get-setting(COLOUR_SECONDARY)), $alpha: 1);
 $success-color: color("green", "base") !default;
 $error-color: color("red", "base") !default;
 $link-color: color("light-blue", "darken-1") !default;
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index a895fa0518dfab756c6643e47558ce6135484be0..17e71cc0c4d928e52bd0ecdd3f24c801b1574c3d 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -33,7 +33,6 @@ urlpatterns = [
     path("impersonate/", include("impersonate.urls")),
     path("__i18n__/", include("django.conf.urls.i18n")),
     path("select2/", include("django_select2.urls")),
-    path("settings/", include("dbsettings.urls")),
 ]
 
 # Serve static files from STATIC_ROOT to make it work with runserver
diff --git a/aleksis/core/util/sass_helpers.py b/aleksis/core/util/sass_helpers.py
index 0c883ffd42ca22a3de6e8758890819f1fd96814d..7c86f6337dc8d8dfaa89eb958737a3bf9a16c64f 100644
--- a/aleksis/core/util/sass_helpers.py
+++ b/aleksis/core/util/sass_helpers.py
@@ -1,8 +1,9 @@
+from django.conf import settings
+
 from colour import web2hex
+from constance import config
 from sass import SassColor
 
-from aleksis.core.db_settings import theme_settings
-
 
 def get_colour(html_colour: str) -> SassColor:
     rgb = web2hex(html_colour, force_long=True)[1:]
@@ -11,5 +12,5 @@ def get_colour(html_colour: str) -> SassColor:
     return SassColor(r, g, b, 255)
 
 
-def get_theme_setting(setting: str) -> str:
-    return getattr(theme_settings, setting, "")
+def get_setting(setting: str) -> str:
+    return getattr(config, setting, "") or getattr(settings, setting, "")
diff --git a/poetry.lock b/poetry.lock
index 4c091fe70c03b2ae1ba6f84157c3d5b05c19906e..9d000b9d8897277815c951e71190cd424cd8328e 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -259,11 +259,22 @@ Django = ">=1.8"
 
 [[package]]
 category = "main"
-description = "Application settings whose values can be updated while a project is up and running."
-name = "django-dbsettings"
+description = "Django live settings with pluggable backends, including Redis."
+name = "django-constance"
 optional = false
 python-versions = "*"
-version = "1.0.0"
+version = "2.5.0"
+
+[package.dependencies]
+six = "*"
+
+[package.dependencies.django-picklefield]
+optional = true
+version = "*"
+
+[package.extras]
+database = ["django-picklefield"]
+redis = ["redis"]
 
 [[package]]
 category = "main"
@@ -419,14 +430,24 @@ version = "3.0.1"
 Django = ">=1.11.3"
 babel = "*"
 
-[package.dependencies.phonenumbers]
-optional = true
-version = ">=7.0.2"
-
 [package.extras]
 phonenumbers = ["phonenumbers (>=7.0.2)"]
 phonenumberslite = ["phonenumberslite (>=7.0.2)"]
 
+[[package]]
+category = "main"
+description = "Pickled object field for Django"
+name = "django-picklefield"
+optional = false
+python-versions = "*"
+version = "2.0"
+
+[package.dependencies]
+Django = ">=1.11"
+
+[package.extras]
+tests = ["tox"]
+
 [[package]]
 category = "main"
 description = "SASS processor to compile SCSS files into *.css, while rendering, or offline."
@@ -1650,7 +1671,7 @@ testing = ["pathlib2", "contextlib2", "unittest2"]
 ldap = ["django-auth-ldap"]
 
 [metadata]
-content-hash = "40917beab5394838573def3e2c8a6719f37040d826df74b3cdc9b52262ad0290"
+content-hash = "bca26513f4520039661e719a675e767eef8a19273d1430cd0810ea8a10ea40da"
 python-versions = "^3.7"
 
 [metadata.files]
@@ -1774,8 +1795,9 @@ django-bulk-update = [
     {file = "django-bulk-update-2.2.0.tar.gz", hash = "sha256:5ab7ce8a65eac26d19143cc189c0f041d5c03b9d1b290ca240dc4f3d6aaeb337"},
     {file = "django_bulk_update-2.2.0-py2.py3-none-any.whl", hash = "sha256:49a403392ae05ea872494d74fb3dfa3515f8df5c07cc277c3dc94724c0ee6985"},
 ]
-django-dbsettings = [
-    {file = "django-dbsettings-1.0.0.tar.gz", hash = "sha256:42b04dffd3bc90d91718c822f1e0212d9368e8efe340f7ef09517b5fb1cf49f5"},
+django-constance = [
+    {file = "django-constance-2.5.0.tar.gz", hash = "sha256:c47db4abd5788584115db681c0e8fef8ff870d49af90aa359076f0833a537199"},
+    {file = "django_constance-2.5.0-py2.py3-none-any.whl", hash = "sha256:1b536d153c168ef548fea5bdcdea84afab83892e2163ec4e896557a3139effd7"},
 ]
 django-debug-toolbar = [
     {file = "django-debug-toolbar-2.1.tar.gz", hash = "sha256:24c157bc6c0e1648e0a6587511ecb1b007a00a354ce716950bff2de12693e7a8"},
@@ -1828,6 +1850,10 @@ django-phonenumber-field = [
     {file = "django-phonenumber-field-3.0.1.tar.gz", hash = "sha256:794ebbc3068a7af75aa72a80cb0cec67e714ff8409a965968040f1fd210b2d97"},
     {file = "django_phonenumber_field-3.0.1-py3-none-any.whl", hash = "sha256:1ab19f723928582fed412bd9844221fa4ff466276d8526b8b4a9913ee1487c5e"},
 ]
+django-picklefield = [
+    {file = "django-picklefield-2.0.tar.gz", hash = "sha256:f1733a8db1b6046c0d7d738e785f9875aa3c198215de11993463a9339aa4ea24"},
+    {file = "django_picklefield-2.0-py2.py3-none-any.whl", hash = "sha256:9052f2dcf4882c683ce87b4356f29b4d014c0dad645b6906baf9f09571f52bc8"},
+]
 django-sass-processor = [
     {file = "django-sass-processor-0.8.tar.gz", hash = "sha256:e039551994feaaba6fcf880412b25a772dd313162a34cbb4289814988cfae340"},
 ]
diff --git a/pyproject.toml b/pyproject.toml
index a1b518307a593aaca9af20c67dc0027166e5a8cc..6f585b411b97f15aebca3d7cef0df53e3e6b786b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -48,8 +48,8 @@ django_select2 = "^7.1"
 requests = "^2.22"
 django-two-factor-auth = { version = "^1.10.0", extras = [ "YubiKey", "phonenumbers", "Call", "SMS" ] }
 django-yarnpkg = "^6.0"
-django-dbsettings = "^1.0.0"
 django-material = "^1.6.0"
+django-constance = {version = "^2.5.0", extras = ["database"]}
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]