diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py
index 888926e5871ea70d4cebd9d8b7b0eb33fb4aa8cd..8e5233c531193f1c9bbccea011ebdc3f4b765c66 100644
--- a/aleksis/core/forms.py
+++ b/aleksis/core/forms.py
@@ -10,7 +10,7 @@ from dynamic_preferences.forms import PreferenceForm
 from material import Fieldset, Layout, Row
 
 from .mixins import ExtensibleForm
-from .models import Announcement, Group, Person
+from .models import Announcement, Group, GroupType, Person
 from .registries import (
     group_preferences_registry,
     person_preferences_registry,
@@ -125,7 +125,7 @@ class EditGroupForm(ExtensibleForm):
     """Form to edit an existing group in the frontend."""
 
     layout = Layout(
-        Fieldset(_("Common data"), "name", "short_name"),
+        Fieldset(_("Common data"), "name", "short_name", "group_type"),
         Fieldset(_("Persons"), "members", "owners", "parent_groups"),
     )
 
@@ -280,3 +280,11 @@ class GroupPreferenceForm(PreferenceForm):
     """Form to edit preferences valid for members of a group."""
 
     registry = group_preferences_registry
+
+
+class EditGroupTypeForm(forms.ModelForm):
+    """Form to manage group types."""
+
+    class Meta:
+        model = GroupType
+        exclude = []
diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py
index c91287d016f24487163a332376b2cc95a9a78cf0..4767a8f3492c7401a22164fe25f54da4ec9fd702 100644
--- a/aleksis/core/menus.py
+++ b/aleksis/core/menus.py
@@ -153,6 +153,17 @@ MENUS = {
                         ("aleksis.core.util.predicates.permission_validator", "core.view_groups")
                     ],
                 },
+                {
+                    "name": _("Group types"),
+                    "url": "group_types",
+                    "icon": "category",
+                    "validators": [
+                        (
+                            "aleksis.core.util.predicates.permission_validator",
+                            "core.view_group_type",
+                        )
+                    ],
+                },
                 {
                     "name": _("Persons and accounts"),
                     "url": "persons_accounts",
diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py
index 5753bbc5374e03dccb97b935b1eba6ae65f04239..866b6a3ee08aa1e3fdb2e3366bcb4f26eaf07376 100644
--- a/aleksis/core/rules.py
+++ b/aleksis/core/rules.py
@@ -1,6 +1,6 @@
 from rules import add_perm, always_allow
 
-from .models import Announcement, Group, Person
+from .models import Announcement, Group, GroupType, Person
 from .util.predicates import (
     has_any_object,
     has_global_perm,
@@ -194,3 +194,21 @@ change_group_preferences = has_person & (
     | is_group_owner
 )
 add_perm("core.change_group_preferences", change_group_preferences)
+
+# Edit group type
+edit_group_type_predicate = has_person & (
+    has_global_perm("core.change_group_type") | has_object_perm("core.change_group_type")
+)
+add_perm("core.edit_group_type", edit_group_type_predicate)
+
+# Delete group type
+delete_group_type_predicate = has_person & (
+    has_global_perm("core.delete_group_type") | has_object_perm("core.delete_group_type")
+)
+add_perm("core.delete_group_type", delete_group_type_predicate)
+
+# View group types
+view_group_type_predicate = has_person & (
+    has_global_perm("core.view_grouptype") | has_any_object("core.view_grouptype", GroupType)
+)
+add_perm("core.view_grouptype", view_group_type_predicate)
diff --git a/aleksis/core/tables.py b/aleksis/core/tables.py
index 7bc4e4e14741d7b5bc0f6c878749511a52e95252..ea5cf05d5470c6cf541c0b41f7d322d6091ae607 100644
--- a/aleksis/core/tables.py
+++ b/aleksis/core/tables.py
@@ -1,3 +1,5 @@
+from django.utils.translation import gettext_lazy as _
+
 import django_tables2 as tables
 from django_tables2.utils import A
 
@@ -20,3 +22,16 @@ class GroupsTable(tables.Table):
 
     name = tables.LinkColumn("group_by_id", args=[A("id")])
     short_name = tables.LinkColumn("group_by_id", args=[A("id")])
+
+
+class GroupTypesTable(tables.Table):
+    """Table to list group types."""
+
+    class Meta:
+        attrs = {"class": "table table-striped table-bordered table-hover table-responsive-xl"}
+
+    name = tables.LinkColumn("edit_group_type_by_id", args=[A("id")])
+    description = tables.LinkColumn("edit_group_type_by_id", args=[A("id")])
+    delete = tables.LinkColumn(
+        "delete_group_type_by_id", args=[A("id")], verbose_name=_("Delete"), text=_("Delete")
+    )
diff --git a/aleksis/core/templates/core/edit_group_type.html b/aleksis/core/templates/core/edit_group_type.html
new file mode 100644
index 0000000000000000000000000000000000000000..c857de98b893cf243c3c108f3c76b5aa142e6037
--- /dev/null
+++ b/aleksis/core/templates/core/edit_group_type.html
@@ -0,0 +1,17 @@
+{# -*- engine:django -*- #}
+
+{% extends "core/base.html" %}
+{% load material_form i18n %}
+
+{% block browser_title %}{% blocktrans %}Edit group type{% endblocktrans %}{% endblock %}
+{% block page_title %}{% blocktrans %}Edit group type{% endblocktrans %}{% endblock %}
+
+{% block content %}
+
+  <form method="post">
+    {% csrf_token %}
+    {% form form=edit_group_type_form %}{% endform %}
+    {% include "core/save_button.html" %}
+  </form>
+
+{% endblock %}
diff --git a/aleksis/core/templates/core/group_types.html b/aleksis/core/templates/core/group_types.html
new file mode 100644
index 0000000000000000000000000000000000000000..6344416b1a8292fd5400e57d63c056e1b2839ef8
--- /dev/null
+++ b/aleksis/core/templates/core/group_types.html
@@ -0,0 +1,18 @@
+{# -*- engine:django -*- #}
+
+{% extends "core/base.html" %}
+
+{% load i18n %}
+{% load render_table from django_tables2 %}
+
+{% block browser_title %}{% blocktrans %}Group types{% endblocktrans %}{% endblock %}
+{% block page_title %}{% blocktrans %}Group types{% endblocktrans %}{% endblock %}
+
+{% block content %}
+  <a class="btn green waves-effect waves-light" href="{% url 'create_group_type' %}">
+    <i class="material-icons left">add</i>
+    {% trans "Create group type" %}
+  </a>
+
+  {% render_table group_types_table %}
+{% endblock %}
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index ad35fa8c5de7f5bf4045c28fe241aecec74235ed..47cb1d63c85c41b562e54909d553febac99c7625 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -37,6 +37,14 @@ urlpatterns = [
         views.notification_mark_read,
         name="notification_mark_read",
     ),
+    path("groups/group_type/create", views.edit_group_type, name="create_group_type"),
+    path(
+        "groups/group_type/<int:id_>/delete",
+        views.delete_group_type,
+        name="delete_group_type_by_id",
+    ),
+    path("groups/group_type/<int:id_>/edit", views.edit_group_type, name="edit_group_type_by_id"),
+    path("groups/group_types", views.group_types, name="group_types"),
     path("announcements/", views.announcements, name="announcements"),
     path("announcement/create/", views.announcement_form, name="add_announcement"),
     path("announcement/edit/<int:id_>/", views.announcement_form, name="edit_announcement"),
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 2b856a04846196b73190778c7eee71c02c3d4ad0..2a6a0ec6de5315f22fdd3a3fceb54dbed4aafe94 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -21,19 +21,20 @@ from .forms import (
     AnnouncementForm,
     ChildGroupsForm,
     EditGroupForm,
+    EditGroupTypeForm,
     EditPersonForm,
     GroupPreferenceForm,
     PersonPreferenceForm,
     PersonsAccountsFormSet,
     SitePreferenceForm,
 )
-from .models import Announcement, DashboardWidget, Group, Notification, Person
+from .models import Announcement, DashboardWidget, Group, GroupType, Notification, Person
 from .registries import (
     group_preferences_registry,
     person_preferences_registry,
     site_preferences_registry,
 )
-from .tables import GroupsTable, PersonsTable
+from .tables import GroupsTable, GroupTypesTable, PersonsTable
 from .util import messages
 from .util.apps import AppConfig
 from .util.core_helpers import objectgetter_optional
@@ -444,3 +445,57 @@ def preferences(
     context["instance"] = instance
 
     return render(request, "dynamic_preferences/form.html", context)
+
+
+@permission_required("core.edit_group_type", fn=objectgetter_optional(GroupType, None, False))
+def edit_group_type(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse:
+    """View to edit or create a group_type."""
+    context = {}
+
+    group_type = objectgetter_optional(GroupType, None, False)(request, id_)
+    context["group_type"] = group_type
+
+    if id_:
+        # Edit form for existing group_type
+        edit_group_type_form = EditGroupTypeForm(request.POST or None, instance=group_type)
+    else:
+        # Empty form to create a new group_type
+        edit_group_type_form = EditGroupTypeForm(request.POST or None)
+
+    if request.method == "POST":
+        if edit_group_type_form.is_valid():
+            edit_group_type_form.save(commit=True)
+
+            messages.success(request, _("The group_type has been saved."))
+
+            return redirect("group_types")
+
+    context["edit_group_type_form"] = edit_group_type_form
+
+    return render(request, "core/edit_group_type.html", context)
+
+
+@permission_required("core.view_grouptype")
+def group_types(request: HttpRequest) -> HttpResponse:
+    """List view for listing all group types."""
+    context = {}
+
+    # Get all group types
+    group_types = get_objects_for_user(request.user, "core.view_grouptype", GroupType)
+
+    # Build table
+    group_types_table = GroupTypesTable(group_types)
+    RequestConfig(request).configure(group_types_table)
+    context["group_types_table"] = group_types_table
+
+    return render(request, "core/group_types.html", context)
+
+
+@permission_required("core.delete_group_type", fn=objectgetter_optional(GroupType, None, False))
+def delete_group_type(request: HttpRequest, id_: int) -> HttpResponse:
+    """View to delete an group_type."""
+    group_type = objectgetter_optional(GroupType, None, False)(request, id_)
+    group_type.delete()
+    messages.success(request, _("The group type has been deleted."))
+
+    return redirect("group_types")