diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 282575f36d245ab7835f6c2e168528d401d76044..7f1a51b6ef491c545742e23cd631a761b25662cb 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -13,6 +13,7 @@ Changed
 ~~~~~~~
 
 * Use new icon set inside of models and templates
+* Run full register printout generation in background 
 
 Fixed
 ~~~~~
diff --git a/aleksis/apps/alsijil/tasks.py b/aleksis/apps/alsijil/tasks.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa8de7b5a075b9a1ae37b129b1870482966577a5
--- /dev/null
+++ b/aleksis/apps/alsijil/tasks.py
@@ -0,0 +1,182 @@
+from copy import deepcopy
+from datetime import date, timedelta
+
+from django.db.models import Q
+from django.utils.translation import gettext as _
+
+from calendarweek import CalendarWeek
+from celery.result import allow_join_result
+from celery.states import SUCCESS
+
+from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
+from aleksis.core.models import Group, PDFFile
+from aleksis.core.util.celery_progress import ProgressRecorder, recorded_task
+from aleksis.core.util.pdf import generate_pdf_from_template
+
+from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
+
+
+@recorded_task
+def generate_full_register_printout(group: int, file_object: int, recorder: ProgressRecorder):
+    """Generate a full register printout as PDF for a group."""
+    context = {}
+
+    _number_of_steps = 8
+
+    recorder.set_progress(1, _number_of_steps, _("Load data ..."))
+
+    group = Group.objects.get(pk=group)
+    file_object = PDFFile.objects.get(pk=file_object)
+
+    groups_q = (
+        Q(lesson_period__lesson__groups=group)
+        | Q(lesson_period__lesson__groups__parent_groups=group)
+        | Q(extra_lesson__groups=group)
+        | Q(extra_lesson__groups__parent_groups=group)
+        | Q(event__groups=group)
+        | Q(event__groups__parent_groups=group)
+    )
+    personal_notes = (
+        PersonalNote.objects.prefetch_related(
+            "lesson_period__substitutions", "lesson_period__lesson__teachers"
+        )
+        .not_empty()
+        .filter(groups_q)
+        .filter(groups_of_person=group)
+    )
+    documentations = LessonDocumentation.objects.not_empty().filter(groups_q)
+
+    recorder.set_progress(2, _number_of_steps, _("Sort data ..."))
+
+    sorted_documentations = {"extra_lesson": {}, "event": {}, "lesson_period": {}}
+    sorted_personal_notes = {"extra_lesson": {}, "event": {}, "lesson_period": {}, "person": {}}
+    for documentation in documentations:
+        key = documentation.register_object.label_
+        sorted_documentations[key][documentation.register_object_key] = documentation
+
+    for note in personal_notes:
+        key = note.register_object.label_
+        sorted_personal_notes[key].setdefault(note.register_object_key, [])
+        sorted_personal_notes[key][note.register_object_key].append(note)
+        sorted_personal_notes["person"].setdefault(note.person.pk, [])
+        sorted_personal_notes["person"][note.person.pk].append(note)
+
+    recorder.set_progress(3, _number_of_steps, _("Load lesson data ..."))
+
+    # Get all lesson periods for the selected group
+    lesson_periods = LessonPeriod.objects.filter_group(group).distinct()
+    events = Event.objects.filter_group(group).distinct()
+    extra_lessons = ExtraLesson.objects.filter_group(group).distinct()
+    weeks = CalendarWeek.weeks_within(group.school_term.date_start, group.school_term.date_end)
+
+    register_objects_by_day = {}
+    for extra_lesson in extra_lessons:
+        day = extra_lesson.date
+        register_objects_by_day.setdefault(day, []).append(
+            (
+                extra_lesson,
+                sorted_documentations["extra_lesson"].get(extra_lesson.pk),
+                sorted_personal_notes["extra_lesson"].get(extra_lesson.pk, []),
+                None,
+            )
+        )
+
+    for event in events:
+        day_number = (event.date_end - event.date_start).days + 1
+        for i in range(day_number):
+            day = event.date_start + timedelta(days=i)
+            event_copy = deepcopy(event)
+            event_copy.annotate_day(day)
+            register_objects_by_day.setdefault(day, []).append(
+                (
+                    event_copy,
+                    sorted_documentations["event"].get(event.pk),
+                    sorted_personal_notes["event"].get(event.pk, []),
+                    None,
+                )
+            )
+
+    recorder.set_progress(4, _number_of_steps, _("Sort lesson data ..."))
+
+    weeks = CalendarWeek.weeks_within(
+        group.school_term.date_start,
+        group.school_term.date_end,
+    )
+
+    for lesson_period in lesson_periods:
+        for week in weeks:
+            day = week[lesson_period.period.weekday]
+
+            if (
+                lesson_period.lesson.validity.date_start
+                <= day
+                <= lesson_period.lesson.validity.date_end
+            ):
+                filtered_documentation = sorted_documentations["lesson_period"].get(
+                    f"{lesson_period.pk}_{week.week}_{week.year}"
+                )
+                filtered_personal_notes = sorted_personal_notes["lesson_period"].get(
+                    f"{lesson_period.pk}_{week.week}_{week.year}", []
+                )
+
+                substitution = lesson_period.get_substitution(week)
+
+                register_objects_by_day.setdefault(day, []).append(
+                    (lesson_period, filtered_documentation, filtered_personal_notes, substitution)
+                )
+
+    recorder.set_progress(5, _number_of_steps, _("Load statistics ..."))
+
+    persons = group.members.prefetch_related(None).select_related(None)
+    persons = group.generate_person_list_with_class_register_statistics(persons)
+
+    prefetched_persons = []
+    for person in persons:
+        person.filtered_notes = sorted_personal_notes["person"].get(person.pk, [])
+        prefetched_persons.append(person)
+
+    context["school_term"] = group.school_term
+    context["persons"] = prefetched_persons
+    context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
+    context["excuse_types_not_absent"] = ExcuseType.objects.filter(count_as_absent=False)
+    context["extra_marks"] = ExtraMark.objects.all()
+    context["group"] = group
+    context["weeks"] = weeks
+    context["register_objects_by_day"] = register_objects_by_day
+    context["register_objects"] = list(lesson_periods) + list(events) + list(extra_lessons)
+    context["today"] = date.today()
+    context["lessons"] = (
+        group.lessons.all()
+        .select_related(None)
+        .prefetch_related(None)
+        .select_related("validity", "subject")
+        .prefetch_related("teachers", "lesson_periods")
+    )
+    context["child_groups"] = (
+        group.child_groups.all()
+        .select_related(None)
+        .prefetch_related(None)
+        .prefetch_related(
+            "lessons",
+            "lessons__validity",
+            "lessons__subject",
+            "lessons__teachers",
+            "lessons__lesson_periods",
+        )
+    )
+
+    recorder.set_progress(6, _number_of_steps, _("Generate template ..."))
+
+    file_object, result = generate_pdf_from_template(
+        "alsijil/print/full_register.html", context, file_object=file_object
+    )
+
+    recorder.set_progress(7, _number_of_steps, _("Generate PDF ..."))
+
+    with allow_join_result():
+        result.wait()
+        file_object.refresh_from_db()
+        if not result.status == SUCCESS and file_object.file:
+            raise Exception(_("PDF generation failed"))
+
+    recorder.set_progress(8, _number_of_steps)
diff --git a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html
index 859f03d53da39edcf267c682de809c2fea6eb54e..c8d71ff3f6b2162814f72a83cfbc14fa8c6f0b1b 100644
--- a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html
+++ b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html
@@ -17,8 +17,8 @@
     <h5>{{ school_term }}</h5>
     <p>({{ school_term.date_start }}–{{ school_term.date_end }})</p>
     {% static "img/aleksis-banner.svg" as aleksis_banner %}
-    <img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}"
-         alt="{{ request.site.preferences.general__title }} – Logo" class="max-size-600 center">
+    <img src="{% firstof SITE_PREFERENCES.theme__logo.url aleksis_banner %}"
+         alt="{{ SITE_PREFERENCES.general__title }} – Logo" class="max-size-600 center">
     <h4 id="group-desc">
       {{ group.name }}
     </h4>
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index 7b5ebec01cb9e1cff9677bb74bbfdf1ee99677e4..1aa04dd2acd2e5ce8e0a325b66eb51a9d78db348 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -1,6 +1,6 @@
 from contextlib import nullcontext
 from copy import deepcopy
-from datetime import date, datetime, timedelta
+from datetime import datetime, timedelta
 from typing import Any, Dict, Optional
 
 from django.apps import apps
@@ -37,10 +37,10 @@ from aleksis.core.mixins import (
     AdvancedEditView,
     SuccessNextMixin,
 )
-from aleksis.core.models import Group, Person, SchoolTerm
+from aleksis.core.models import Group, PDFFile, Person, SchoolTerm
 from aleksis.core.util import messages
+from aleksis.core.util.celery_progress import render_progress_page
 from aleksis.core.util.core_helpers import get_site_preferences, objectgetter_optional
-from aleksis.core.util.pdf import render_pdf
 from aleksis.core.util.predicates import check_global_permission
 
 from .filters import PersonalNoteFilter
@@ -58,14 +58,7 @@ from .forms import (
     RegisterObjectActionForm,
     SelectForm,
 )
-from .models import (
-    ExcuseType,
-    ExtraMark,
-    GroupRole,
-    GroupRoleAssignment,
-    LessonDocumentation,
-    PersonalNote,
-)
+from .models import ExcuseType, ExtraMark, GroupRole, GroupRoleAssignment, PersonalNote
 from .tables import (
     ExcuseTypeTable,
     ExtraMarkTable,
@@ -74,6 +67,7 @@ from .tables import (
     RegisterObjectSelectTable,
     RegisterObjectTable,
 )
+from .tasks import generate_full_register_printout
 from .util.alsijil_helpers import (
     annotate_documentations,
     generate_list_of_all_register_objects,
@@ -642,138 +636,36 @@ def week_view(
     "alsijil.view_full_register_rule", fn=objectgetter_optional(Group, None, False)
 )
 def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
-    context = {}
-
     group = get_object_or_404(Group, pk=id_)
-    groups_q = (
-        Q(lesson_period__lesson__groups=group)
-        | Q(lesson_period__lesson__groups__parent_groups=group)
-        | Q(extra_lesson__groups=group)
-        | Q(extra_lesson__groups__parent_groups=group)
-        | Q(event__groups=group)
-        | Q(event__groups__parent_groups=group)
-    )
-    personal_notes = (
-        PersonalNote.objects.prefetch_related(
-            "lesson_period__substitutions", "lesson_period__lesson__teachers"
-        )
-        .not_empty()
-        .filter(groups_q)
-        .filter(groups_of_person=group)
-    )
-    documentations = LessonDocumentation.objects.not_empty().filter(groups_q)
 
-    sorted_documentations = {"extra_lesson": {}, "event": {}, "lesson_period": {}}
-    sorted_personal_notes = {"extra_lesson": {}, "event": {}, "lesson_period": {}, "person": {}}
-    for documentation in documentations:
-        key = documentation.register_object.label_
-        sorted_documentations[key][documentation.register_object_key] = documentation
+    file_object = PDFFile.objects.create()
 
-    for note in personal_notes:
-        key = note.register_object.label_
-        sorted_personal_notes[key].setdefault(note.register_object_key, [])
-        sorted_personal_notes[key][note.register_object_key].append(note)
-        sorted_personal_notes["person"].setdefault(note.person.pk, [])
-        sorted_personal_notes["person"][note.person.pk].append(note)
-
-    # Get all lesson periods for the selected group
-    lesson_periods = LessonPeriod.objects.filter_group(group).distinct()
-    events = Event.objects.filter_group(group).distinct()
-    extra_lessons = ExtraLesson.objects.filter_group(group).distinct()
-    weeks = CalendarWeek.weeks_within(group.school_term.date_start, group.school_term.date_end)
-
-    register_objects_by_day = {}
-    for extra_lesson in extra_lessons:
-        day = extra_lesson.date
-        register_objects_by_day.setdefault(day, []).append(
-            (
-                extra_lesson,
-                sorted_documentations["extra_lesson"].get(extra_lesson.pk),
-                sorted_personal_notes["extra_lesson"].get(extra_lesson.pk, []),
-                None,
-            )
-        )
+    redirect_url = reverse("redirect_to_pdf_file", args=[file_object.pk])
 
-    for event in events:
-        day_number = (event.date_end - event.date_start).days + 1
-        for i in range(day_number):
-            day = event.date_start + timedelta(days=i)
-            event_copy = deepcopy(event)
-            event_copy.annotate_day(day)
-            register_objects_by_day.setdefault(day, []).append(
-                (
-                    event_copy,
-                    sorted_documentations["event"].get(event.pk),
-                    sorted_personal_notes["event"].get(event.pk, []),
-                    None,
-                )
-            )
+    result = generate_full_register_printout.delay(group.pk, file_object.pk)
 
-    weeks = CalendarWeek.weeks_within(
-        group.school_term.date_start,
-        group.school_term.date_end,
+    back_url = request.GET.get("back", "")
+    back_url_is_safe = url_has_allowed_host_and_scheme(
+        url=back_url,
+        allowed_hosts={request.get_host()},
+        require_https=request.is_secure(),
     )
-
-    for lesson_period in lesson_periods:
-        for week in weeks:
-            day = week[lesson_period.period.weekday]
-
-            if (
-                lesson_period.lesson.validity.date_start
-                <= day
-                <= lesson_period.lesson.validity.date_end
-            ):
-                filtered_documentation = sorted_documentations["lesson_period"].get(
-                    f"{lesson_period.pk}_{week.week}_{week.year}"
-                )
-                filtered_personal_notes = sorted_personal_notes["lesson_period"].get(
-                    f"{lesson_period.pk}_{week.week}_{week.year}", []
-                )
-
-                substitution = lesson_period.get_substitution(week)
-
-                register_objects_by_day.setdefault(day, []).append(
-                    (lesson_period, filtered_documentation, filtered_personal_notes, substitution)
-                )
-
-    persons = group.members.prefetch_related(None).select_related(None)
-    persons = group.generate_person_list_with_class_register_statistics(persons)
-
-    prefetched_persons = []
-    for person in persons:
-        person.filtered_notes = sorted_personal_notes["person"].get(person.pk, [])
-        prefetched_persons.append(person)
-
-    context["school_term"] = group.school_term
-    context["persons"] = prefetched_persons
-    context["excuse_types"] = ExcuseType.objects.filter(count_as_absent=True)
-    context["excuse_types_not_absent"] = ExcuseType.objects.filter(count_as_absent=False)
-    context["extra_marks"] = ExtraMark.objects.all()
-    context["group"] = group
-    context["weeks"] = weeks
-    context["register_objects_by_day"] = register_objects_by_day
-    context["register_objects"] = list(lesson_periods) + list(events) + list(extra_lessons)
-    context["today"] = date.today()
-    context["lessons"] = (
-        group.lessons.all()
-        .select_related(None)
-        .prefetch_related(None)
-        .select_related("validity", "subject")
-        .prefetch_related("teachers", "lesson_periods")
-    )
-    context["child_groups"] = (
-        group.child_groups.all()
-        .select_related(None)
-        .prefetch_related(None)
-        .prefetch_related(
-            "lessons",
-            "lessons__validity",
-            "lessons__subject",
-            "lessons__teachers",
-            "lessons__lesson_periods",
-        )
+    if not back_url_is_safe:
+        back_url = reverse("my_groups")
+
+    return render_progress_page(
+        request,
+        result,
+        title=_("Generate full register printout for {}").format(group),
+        progress_title=_("Generate full register printout …"),
+        success_message=_("The printout has been generated successfully."),
+        error_message=_("There was a problem while generating the printout."),
+        redirect_on_success_url=redirect_url,
+        back_url=back_url,
+        button_title=_("Download PDF"),
+        button_url=redirect_url,
+        button_icon="picture_as_pdf",
     )
-    return render_pdf(request, "alsijil/print/full_register.html", context)
 
 
 @permission_required("alsijil.view_my_students_rule")
diff --git a/pyproject.toml b/pyproject.toml
index bb15d014a4509f016d7d5c7e940dcbc6bb249c08..a3a4aea8d9b8f3cfd286cab366900bcce9ee5b9c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -48,7 +48,7 @@ secondary = true
 
 [tool.poetry.dependencies]
 python = "^3.9"
-aleksis-core = "^2.11"
+aleksis-core = "^2.12"
 aleksis-app-chronos = "^2.2"
 aleksis-app-stoelindeling = { version = "^1.0", optional = true }