diff --git a/api/funkwhale_api/audio/dynamic_preferences_registry.py b/api/funkwhale_api/audio/dynamic_preferences_registry.py
index 8f9b096b03aa5904ee473ff5ade3360074b0cd4a..3dfcc85589450ef50adea520922cde16d3fd3470 100644
--- a/api/funkwhale_api/audio/dynamic_preferences_registry.py
+++ b/api/funkwhale_api/audio/dynamic_preferences_registry.py
@@ -14,3 +14,12 @@ class ChannelsEnabled(types.BooleanPreference):
         "If disabled, the channels feature will be completely switched off, "
         "and users won't be able to create channels or subscribe to them."
     )
+
+
+@global_preferences_registry.register
+class MaxChannels(types.IntegerPreference):
+    show_in_api = True
+    section = audio
+    default = 20
+    name = "max_channels"
+    verbose_name = "Max channels allowed per user"
diff --git a/api/funkwhale_api/audio/serializers.py b/api/funkwhale_api/audio/serializers.py
index 3f953436cb363f0723912e822d56353141f9685d..70c51587e998c87fa6def0e64b416fbf9dd005c0 100644
--- a/api/funkwhale_api/audio/serializers.py
+++ b/api/funkwhale_api/audio/serializers.py
@@ -5,6 +5,7 @@ from rest_framework import serializers
 from funkwhale_api.common import serializers as common_serializers
 from funkwhale_api.common import utils as common_utils
 from funkwhale_api.common import locales
+from funkwhale_api.common import preferences
 from funkwhale_api.federation import serializers as federation_serializers
 from funkwhale_api.federation import utils as federation_utils
 from funkwhale_api.music import models as music_models
@@ -59,6 +60,11 @@ class ChannelCreateSerializer(serializers.Serializer):
     metadata = serializers.DictField(required=False)
 
     def validate(self, validated_data):
+        existing_channels = self.context["actor"].owned_channels.count()
+        if existing_channels >= preferences.get("audio__max_channels"):
+            raise serializers.ValidationError(
+                "You have reached the maximum amount of allowed channels"
+            )
         validated_data = super().validate(validated_data)
         metadata = validated_data.pop("metadata", {})
         if validated_data["content_category"] == "podcast":
diff --git a/api/funkwhale_api/audio/views.py b/api/funkwhale_api/audio/views.py
index 09ae6d0cfafd1e644ccab0078f8be3ef41f9ddef..91331d2f22cc60f47215dcc54de5c483761e84e9 100644
--- a/api/funkwhale_api/audio/views.py
+++ b/api/funkwhale_api/audio/views.py
@@ -138,6 +138,8 @@ class ChannelViewSet(
             "update",
             "partial_update",
         ]
+        if self.request.user.is_authenticated:
+            context["actor"] = self.request.user.actor
         return context
 
 
diff --git a/api/tests/audio/test_serializers.py b/api/tests/audio/test_serializers.py
index 430673d6349099f0fe0e3ae138393c6298885283..2ada89653e60f2363bb339d4153cd318574f1011 100644
--- a/api/tests/audio/test_serializers.py
+++ b/api/tests/audio/test_serializers.py
@@ -23,7 +23,9 @@ def test_channel_serializer_create(factories):
         "content_category": "other",
     }
 
-    serializer = serializers.ChannelCreateSerializer(data=data)
+    serializer = serializers.ChannelCreateSerializer(
+        data=data, context={"actor": attributed_to}
+    )
     assert serializer.is_valid(raise_exception=True) is True
 
     channel = serializer.save(attributed_to=attributed_to)
@@ -49,6 +51,26 @@ def test_channel_serializer_create(factories):
     assert channel.library.actor == attributed_to
 
 
+def test_channel_serializer_create_honor_max_channels_setting(factories, preferences):
+    preferences["audio__max_channels"] = 1
+    attributed_to = factories["federation.Actor"](local=True)
+    factories["audio.Channel"](attributed_to=attributed_to)
+    data = {
+        # TODO: cover
+        "name": "My channel",
+        "username": "mychannel",
+        "description": {"text": "This is my channel", "content_type": "text/markdown"},
+        "tags": ["hello", "world"],
+        "content_category": "other",
+    }
+
+    serializer = serializers.ChannelCreateSerializer(
+        data=data, context={"actor": attributed_to}
+    )
+    with pytest.raises(serializers.serializers.ValidationError, match=r".*max.*"):
+        assert serializer.is_valid(raise_exception=True)
+
+
 def test_channel_serializer_create_podcast(factories):
     attributed_to = factories["federation.Actor"](local=True)
 
@@ -62,7 +84,9 @@ def test_channel_serializer_create_podcast(factories):
         "metadata": {"itunes_category": "Sports", "language": "en"},
     }
 
-    serializer = serializers.ChannelCreateSerializer(data=data)
+    serializer = serializers.ChannelCreateSerializer(
+        data=data, context={"actor": attributed_to}
+    )
     assert serializer.is_valid(raise_exception=True) is True
 
     channel = serializer.save(attributed_to=attributed_to)
diff --git a/front/src/views/admin/Settings.vue b/front/src/views/admin/Settings.vue
index fdab614eaf78f95c87fc28342d4418c6dd274b22..7f40a00e3895fa90ce09ebfc627f29c459f6458b 100644
--- a/front/src/views/admin/Settings.vue
+++ b/front/src/views/admin/Settings.vue
@@ -80,6 +80,7 @@ export default {
       let instanceLabel = this.$pgettext('Content/Admin/Menu','Instance information')
       let usersLabel = this.$pgettext('*/*/*/Noun', 'Users')
       let musicLabel = this.$pgettext('*/*/*/Noun', 'Music')
+      let channelsLabel = this.$pgettext('*/*/*', 'Channels')
       let playlistsLabel = this.$pgettext('*/*/*', 'Playlists')
       let federationLabel = this.$pgettext('*/*/*', 'Federation')
       let moderationLabel = this.$pgettext('*/Moderation/*', 'Moderation')
@@ -120,6 +121,14 @@ export default {
             {name: "music__transcoding_cache_duration"},
           ]
         },
+        {
+          label: channelsLabel,
+          id: "channels",
+          settings: [
+            {name: "audio__channels_enabled"},
+            {name: "audio__max_channels"},
+          ]
+        },
         {
           label: playlistsLabel,
           id: "playlists",