diff --git a/calendar_utils/models.py b/calendar_utils/models.py
index 830e5ad35c204a1cbb98ef94b63e802343a26236..ca902a06cbdef2aae67428fc62be82dd2b4f5a6e 100644
--- a/calendar_utils/models.py
+++ b/calendar_utils/models.py
@@ -1,15 +1,17 @@
 import json
 import logging
 from datetime import timedelta
+from functools import partial
 
 import arrow
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.validators import URLValidator, ValidationError
-from django.db import models
+from django.db import models, transaction
 from django.utils.timezone import now
 from icalevents import icalevents
 
 from .parser import process_event_list
+from .tasks import update_calendar_source
 
 logger = logging.getLogger(__name__)
 
@@ -81,7 +83,10 @@ class CalendarMixin(models.Model):
     """
 
     calendar_url = models.URLField(
-        "URL kalendáře ve formátu iCal", blank=True, null=True
+        "URL kalendáře ve formátu iCal",
+        blank=True,
+        null=True,
+        help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
     )
     calendar = models.ForeignKey(
         Calendar, null=True, blank=True, on_delete=models.PROTECT
@@ -136,12 +141,9 @@ class CalendarMixin(models.Model):
                 else:
                     self.calendar = Calendar.objects.create(url=self.calendar_url)
 
-            try:
-                self.calendar.update_source()
-            except:
-                logger.error(
-                    "Calendar update failed for %s", self.calendar.url, exc_info=True
-                )
+            transaction.on_commit(
+                partial(update_calendar_source.delay, self.calendar.id)
+            )
 
         # delete related Calendar when URL is cleared
         if not self.calendar_url and self.calendar:
diff --git a/calendar_utils/tasks.py b/calendar_utils/tasks.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c02cf4076a4be35199be63253e5101c3a14c65d
--- /dev/null
+++ b/calendar_utils/tasks.py
@@ -0,0 +1,12 @@
+from celery import shared_task
+from celery.utils.log import get_task_logger
+
+logger = get_task_logger(__name__)
+
+
+@shared_task
+def update_calendar_source(calendar_id):
+    from .models import Calendar  # noqa circular import
+
+    cal = Calendar.objects.get(id=calendar_id)
+    cal.update_source()
diff --git a/district/migrations/0123_alter_districtcalendarpage_calendar_url_and_more.py b/district/migrations/0123_alter_districtcalendarpage_calendar_url_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..39883b6f463548fd5f26118329e90a2b29106039
--- /dev/null
+++ b/district/migrations/0123_alter_districtcalendarpage_calendar_url_and_more.py
@@ -0,0 +1,52 @@
+# Generated by Django 4.1.10 on 2023-08-23 10:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("district", "0122_remove_districthomepage_shared_tags"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="districtcalendarpage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="districtcenterpage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="districthomepage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="districtpersonpage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+    ]
diff --git a/elections2021/migrations/0055_alter_elections2021calendarpage_calendar_url.py b/elections2021/migrations/0055_alter_elections2021calendarpage_calendar_url.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f2d1120d373025fbb6ce871d87ec855d4006c8b
--- /dev/null
+++ b/elections2021/migrations/0055_alter_elections2021calendarpage_calendar_url.py
@@ -0,0 +1,22 @@
+# Generated by Django 4.1.10 on 2023-08-23 10:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("elections2021", "0054_elections2021articlepage_shared_from_and_more"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="elections2021calendarpage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+    ]
diff --git a/main/migrations/0062_alter_mainpersonpage_calendar_url.py b/main/migrations/0062_alter_mainpersonpage_calendar_url.py
new file mode 100644
index 0000000000000000000000000000000000000000..9acf5bf9c0326db554934791d3324ced3e333dec
--- /dev/null
+++ b/main/migrations/0062_alter_mainpersonpage_calendar_url.py
@@ -0,0 +1,22 @@
+# Generated by Django 4.1.10 on 2023-08-23 10:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("main", "0061_remove_mainhomepage_shared_tags"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="mainpersonpage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+    ]
diff --git a/senat_campaign/migrations/0013_alter_senatcampaignhomepage_calendar_url.py b/senat_campaign/migrations/0013_alter_senatcampaignhomepage_calendar_url.py
new file mode 100644
index 0000000000000000000000000000000000000000..de27f47891c1e70199b955c62578cecff864da8b
--- /dev/null
+++ b/senat_campaign/migrations/0013_alter_senatcampaignhomepage_calendar_url.py
@@ -0,0 +1,22 @@
+# Generated by Django 4.1.10 on 2023-08-23 10:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("senat_campaign", "0012_merge_20230502_1854"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="senatcampaignhomepage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+    ]
diff --git a/uniweb/migrations/0049_alter_uniwebcalendarpage_calendar_url_and_more.py b/uniweb/migrations/0049_alter_uniwebcalendarpage_calendar_url_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..f57f7d75af109569010b824f6c85935892873a45
--- /dev/null
+++ b/uniweb/migrations/0049_alter_uniwebcalendarpage_calendar_url_and_more.py
@@ -0,0 +1,32 @@
+# Generated by Django 4.1.10 on 2023-08-23 10:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("uniweb", "0048_merge_20230816_1107"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="uniwebcalendarpage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="uniwebhomepage",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True,
+                help_text="Kalendář se po uložení stránky aktualizuje na pozadí. U plnějších kalendářů to může trvat i desítky sekund.",
+                null=True,
+                verbose_name="URL kalendáře ve formátu iCal",
+            ),
+        ),
+    ]