diff --git a/InvenTree/InvenTree/config.py b/InvenTree/InvenTree/config.py
index 27722435f3..892e5751c4 100644
--- a/InvenTree/InvenTree/config.py
+++ b/InvenTree/InvenTree/config.py
@@ -1,5 +1,6 @@
 """Helper functions for loading InvenTree configuration options."""
 
+import datetime
 import logging
 import os
 import random
@@ -8,6 +9,8 @@ import string
 from pathlib import Path
 
 logger = logging.getLogger('inventree')
+CONFIG_DATA = None
+CONFIG_LOOKUPS = {}
 
 
 def is_true(x):
@@ -56,8 +59,18 @@ def get_config_file(create=True) -> Path:
     return cfg_filename
 
 
-def load_config_data() -> map:
-    """Load configuration data from the config file."""
+def load_config_data(set_cache: bool = False) -> map:
+    """Load configuration data from the config file.
+
+    Arguments:
+        set_cache(bool): If True, the configuration data will be cached for future use after load.
+    """
+    global CONFIG_DATA
+
+    # use cache if populated
+    # skip cache if cache should be set
+    if CONFIG_DATA is not None and not set_cache:
+        return CONFIG_DATA
 
     import yaml
 
@@ -66,6 +79,10 @@ def load_config_data() -> map:
     with open(cfg_file, 'r') as cfg:
         data = yaml.safe_load(cfg)
 
+    # Set the cache if requested
+    if set_cache:
+        CONFIG_DATA = data
+
     return data
 
 
@@ -82,22 +99,30 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None
         default_value: Value to return if first two options are not provided
         typecast: Function to use for typecasting the value
     """
-    def try_typecasting(value):
+    def try_typecasting(value, source: str):
         """Attempt to typecast the value"""
         if typecast is not None:
             # Try to typecast the value
             try:
-                return typecast(value)
+                val = typecast(value)
+                set_metadata(source)
+                return val
             except Exception as error:
                 logger.error(f"Failed to typecast '{env_var}' with value '{value}' to type '{typecast}' with error {error}")
+        set_metadata(source)
         return value
 
+    def set_metadata(source: str):
+        """Set lookup metadata for the setting."""
+        key = env_var or config_key
+        CONFIG_LOOKUPS[key] = {'env_var': env_var, 'config_key': config_key, 'source': source, 'accessed': datetime.datetime.now()}
+
     # First, try to load from the environment variables
     if env_var is not None:
         val = os.getenv(env_var, None)
 
         if val is not None:
-            return try_typecasting(val)
+            return try_typecasting(val, 'env')
 
     # Next, try to load from configuration file
     if config_key is not None:
@@ -116,10 +141,10 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None
             cfg_data = cfg_data[key]
 
         if result is not None:
-            return try_typecasting(result)
+            return try_typecasting(result, 'yaml')
 
     # Finally, return the default value
-    return try_typecasting(default_value)
+    return try_typecasting(default_value, 'default')
 
 
 def get_boolean_setting(env_var=None, config_key=None, default_value=False):
diff --git a/InvenTree/InvenTree/permissions.py b/InvenTree/InvenTree/permissions.py
index 74d42b6da0..714ff99139 100644
--- a/InvenTree/InvenTree/permissions.py
+++ b/InvenTree/InvenTree/permissions.py
@@ -67,6 +67,14 @@ class RolePermission(permissions.BasePermission):
         return result
 
 
+class IsSuperuser(permissions.IsAdminUser):
+    """Allows access only to superuser users."""
+
+    def has_permission(self, request, view):
+        """Check if the user is a superuser."""
+        return bool(request.user and request.user.is_superuser)
+
+
 def auth_exempt(view_func):
     """Mark a view function as being exempt from auth requirements."""
     def wrapped_view(*args, **kwargs):
diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py
index 5cc4e7ed0c..b85c45a3b4 100644
--- a/InvenTree/InvenTree/settings.py
+++ b/InvenTree/InvenTree/settings.py
@@ -52,7 +52,7 @@ DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
 BASE_DIR = config.get_base_dir()
 
 # Load configuration data
-CONFIG = config.load_config_data()
+CONFIG = config.load_config_data(set_cache=True)
 
 # Default action is to run the system in Debug mode
 # SECURITY WARNING: don't run with debug turned on in production!
diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py
index 50455d7601..d63e4022f4 100644
--- a/InvenTree/InvenTree/urls.py
+++ b/InvenTree/InvenTree/urls.py
@@ -13,7 +13,7 @@ from rest_framework.documentation import include_docs_urls
 
 from build.api import build_api_urls
 from build.urls import build_urls
-from common.api import common_api_urls, settings_api_urls
+from common.api import admin_api_urls, common_api_urls, settings_api_urls
 from common.urls import common_urls
 from company.api import company_api_urls
 from company.urls import (company_urls, manufacturer_part_urls,
@@ -53,6 +53,7 @@ apipatterns = [
     re_path(r'^label/', include(label_api_urls)),
     re_path(r'^report/', include(report_api_urls)),
     re_path(r'^user/', include(user_urls)),
+    re_path(r'^admin/', include(admin_api_urls)),
 
     # Plugin endpoints
     path('', include(plugin_api_urls)),
diff --git a/InvenTree/common/api.py b/InvenTree/common/api.py
index e87194446e..d17108d3a0 100644
--- a/InvenTree/common/api.py
+++ b/InvenTree/common/api.py
@@ -18,9 +18,11 @@ from rest_framework.views import APIView
 import common.models
 import common.serializers
 from InvenTree.api import BulkDeleteMixin
+from InvenTree.config import CONFIG_LOOKUPS
 from InvenTree.helpers import inheritors
 from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI,
                               RetrieveUpdateDestroyAPI)
+from InvenTree.permissions import IsSuperuser
 from plugin.models import NotificationUserSetting
 from plugin.serializers import NotificationUserSettingSerializer
 
@@ -360,6 +362,29 @@ class NewsFeedEntryDetail(NewsFeedMixin, RetrieveUpdateDestroyAPI):
     """Detail view for an individual news feed object."""
 
 
+class ConfigList(ListAPI):
+    """List view for all accessed configurations."""
+
+    queryset = CONFIG_LOOKUPS
+    serializer_class = common.serializers.ConfigSerializer
+    permission_classes = [IsSuperuser, ]
+
+
+class ConfigDetail(RetrieveAPI):
+    """Detail view for an individual configuration."""
+
+    serializer_class = common.serializers.ConfigSerializer
+    permission_classes = [IsSuperuser, ]
+
+    def get_object(self):
+        """Attempt to find a config object with the provided key."""
+        key = self.kwargs['key']
+        value = CONFIG_LOOKUPS.get(key, None)
+        if not value:
+            raise NotFound()
+        return {key: value}
+
+
 settings_api_urls = [
     # User settings
     re_path(r'^user/', include([
@@ -415,3 +440,9 @@ common_api_urls = [
     ])),
 
 ]
+
+admin_api_urls = [
+    # Admin
+    path('config/', ConfigList.as_view(), name='api-config-list'),
+    path('config/<str:key>/', ConfigDetail.as_view(), name='api-config-detail'),
+]
diff --git a/InvenTree/common/serializers.py b/InvenTree/common/serializers.py
index b39d19cfc0..c84d478548 100644
--- a/InvenTree/common/serializers.py
+++ b/InvenTree/common/serializers.py
@@ -222,3 +222,16 @@ class NewsFeedEntrySerializer(InvenTreeModelSerializer):
             'summary',
             'read',
         ]
+
+
+class ConfigSerializer(serializers.Serializer):
+    """Serializer for the InvenTree configuration.
+
+    This is a read-only serializer.
+    """
+
+    def to_representation(self, instance):
+        """Return the configuration data as a dictionary."""
+        if not isinstance(instance, str):
+            instance = list(instance.keys())[0]
+        return {'key': instance, **self.instance[instance]}
diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py
index b305073045..2cf795de24 100644
--- a/InvenTree/common/tests.py
+++ b/InvenTree/common/tests.py
@@ -827,7 +827,7 @@ class NotificationTest(InvenTreeAPITestCase):
         self.assertEqual(NotificationMessage.objects.filter(user=self.user).count(), 3)
 
 
-class LoadingTest(TestCase):
+class CommonTest(InvenTreeAPITestCase):
     """Tests for the common config."""
 
     def test_restart_flag(self):
@@ -844,6 +844,30 @@ class LoadingTest(TestCase):
         # now it should be false again
         self.assertFalse(common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED'))
 
+    def test_config_api(self):
+        """Test config URLs."""
+        # Not superuser
+        self.get(reverse('api-config-list'), expected_code=403)
+
+        # Turn into superuser
+        self.user.is_superuser = True
+        self.user.save()
+
+        # Successfull checks
+        data = [
+            self.get(reverse('api-config-list'), expected_code=200).data[0],                                    # list endpoint
+            self.get(reverse('api-config-detail', kwargs={'key': 'INVENTREE_DEBUG'}), expected_code=200).data,  # detail endpoint
+        ]
+
+        for item in data:
+            self.assertEqual(item['key'], 'INVENTREE_DEBUG')
+            self.assertEqual(item['env_var'], 'INVENTREE_DEBUG')
+            self.assertEqual(item['config_key'], 'debug')
+
+        # Turn into normal user again
+        self.user.is_superuser = False
+        self.user.save()
+
 
 class ColorThemeTest(TestCase):
     """Tests for ColorTheme."""