diff --git a/epicardium/api/control.c b/epicardium/api/control.c
index f599788426eef36c601925f6929bc91e40a83b76..deff433010bf426c1c8a59bcab5450d7787fe55a 100644
--- a/epicardium/api/control.c
+++ b/epicardium/api/control.c
@@ -9,7 +9,7 @@
 #include "tmr.h"
 
 static void __core1_init(void);
-extern void interrupt_trigger(api_int_id_t id);
+extern void interrupt_trigger_sync(api_int_id_t id);
 
 struct core1_info {
 	/* Location of core1's interrupt vector table */
@@ -206,8 +206,13 @@ void core1_boot(void)
 
 void core1_trigger_reset(void)
 {
-	/* Signal core 1 that we intend to load a new payload. */
-	interrupt_trigger(EPIC_INT_RESET);
+	/*
+	 * Signal core 1 that we intend to load a new payload.
+	 *
+	 * This needs to be synchroneous because otherwise we will deadlock
+	 * (Lifecycle task busy-spins and interrupt can never get dispatched).
+	 */
+	interrupt_trigger_sync(EPIC_INT_RESET);
 }
 
 void core1_wait_ready(void)
diff --git a/epicardium/main.c b/epicardium/main.c
index c5e2768c37177f97f6a2388bdfdd8b71f10cd591..89054e9e199f9ade4236e7e270a0068ddec67ce9 100644
--- a/epicardium/main.c
+++ b/epicardium/main.c
@@ -133,6 +133,16 @@ int main(void)
 		    &dispatcher_task_id) != pdPASS) {
 		panic("Failed to create %s task!", "API Dispatcher");
 	}
+	/* Interrupts */
+	if (xTaskCreate(
+		    vInterruptsTask,
+		    (const char *)"Interrupt Dispatcher",
+		    configMINIMAL_STACK_SIZE,
+		    NULL,
+		    tskIDLE_PRIORITY + 2,
+		    NULL) != pdPASS) {
+		panic("Failed to create %s task!", "Interrupt Dispatcher");
+	}
 
 	/* BLE */
 	if (ble_shall_start()) {
diff --git a/epicardium/modules/interrupts.c b/epicardium/modules/interrupts.c
index e33e35769d887fbcc6eafdf79186e7015f88106c..0ead54fc58b1d6341071cd377a8ab239bbbff8e7 100644
--- a/epicardium/modules/interrupts.c
+++ b/epicardium/modules/interrupts.c
@@ -1,4 +1,5 @@
 #include "modules/mutex.h"
+#include "modules/log.h"
 #include "epicardium.h"
 #include "api/interrupt-sender.h"
 #include <assert.h>
@@ -14,11 +15,28 @@ struct interrupt_priv {
 
 static struct interrupt_priv interrupt_data;
 static struct mutex interrupt_mutex;
+static TaskHandle_t interrupts_task;
 
 void interrupt_trigger(api_int_id_t id)
 {
 	assert(id < EPIC_INT_NUM);
 
+	mutex_lock(&interrupt_mutex);
+
+	if (interrupt_data.int_enabled[id]) {
+		interrupt_data.int_pending[id] = true;
+		interrupt_data.has_pending     = true;
+		mutex_unlock(&interrupt_mutex);
+		xTaskNotifyGive(interrupts_task);
+	} else {
+		mutex_unlock(&interrupt_mutex);
+	}
+}
+
+void interrupt_trigger_sync(api_int_id_t id)
+{
+	assert(id < EPIC_INT_NUM);
+
 	mutex_lock(&interrupt_mutex);
 	if (!interrupt_data.int_enabled[id])
 		goto out;
@@ -97,3 +115,42 @@ int epic_interrupt_disable(api_int_id_t int_id)
 	return 0;
 }
 /* }}} */
+
+void vInterruptsTask(void *pvParameters)
+{
+	interrupts_task = xTaskGetCurrentTaskHandle();
+
+	while (true) {
+		mutex_lock(&interrupt_mutex);
+
+		if (!interrupt_data.has_pending) {
+			/* Wait for a wakeup event from interrupt_trigger() */
+			mutex_unlock(&interrupt_mutex);
+			ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+			mutex_lock(&interrupt_mutex);
+		}
+
+		while (!api_interrupt_is_ready()) {
+			mutex_unlock(&interrupt_mutex);
+			vTaskDelay(pdMS_TO_TICKS(5));
+			mutex_lock(&interrupt_mutex);
+		}
+
+		api_int_id_t current_irq = EPIC_INT_NUM;
+		for (size_t i = 0; i < EPIC_INT_NUM; i++) {
+			if (interrupt_data.int_pending[i]) {
+				current_irq                   = i;
+				interrupt_data.int_pending[i] = false;
+				break;
+			}
+		}
+
+		if (current_irq == EPIC_INT_NUM) {
+			interrupt_data.has_pending = false;
+		} else if (interrupt_data.int_enabled[current_irq]) {
+			api_interrupt_trigger(current_irq);
+		}
+
+		mutex_unlock(&interrupt_mutex);
+	}
+}
diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h
index 534901c5ccb6d82a69b7b314e8f3e85da4916616..c26a1ce6cf7a2480eb982d8b80ab1215d4c06278 100644
--- a/epicardium/modules/modules.h
+++ b/epicardium/modules/modules.h
@@ -31,9 +31,11 @@ void return_to_menu(void);
 /* ---------- Interrupts --------------------------------------------------- */
 void interrupt_init(void);
 void interrupt_trigger(api_int_id_t id);
+void interrupt_trigger_sync(api_int_id_t id);
 void interrupt_trigger_unsafe(api_int_id_t id) __attribute__((deprecated(
 	"interrupt_trigger_unsafe() is racy and only exists for legacy code."
 )));
+void vInterruptsTask(void *pvParameters);
 
 /* ---------- Serial ------------------------------------------------------- */
 #define SERIAL_READ_BUFFER_SIZE 128