diff --git a/lib/tunnelbroker/peer-to-peer-message-handler.js b/lib/tunnelbroker/peer-to-peer-message-handler.js
--- a/lib/tunnelbroker/peer-to-peer-message-handler.js
+++ b/lib/tunnelbroker/peer-to-peer-message-handler.js
@@ -3,7 +3,11 @@
 import * as React from 'react';
 
 import { useTunnelbroker } from './tunnelbroker-context.js';
-import { usePeerToPeerMessageHandler } from './use-peer-to-peer-message-handler.js';
+import {
+  useHandleOlmMessageToDevice,
+  usePeerToPeerMessageHandler,
+} from './use-peer-to-peer-message-handler.js';
+import type { InboundP2PMessage } from '../types/sqlite-types.js';
 import type { MessageReceiveConfirmation } from '../types/tunnelbroker/message-receive-confirmation-types.js';
 import {
   deviceToTunnelbrokerMessageTypes,
@@ -14,6 +18,7 @@
   peerToPeerMessageValidator,
   type PeerToPeerMessage,
 } from '../types/tunnelbroker/peer-to-peer-message-types.js';
+import { getConfig } from '../utils/config.js';
 
 type Props = {
   +socketSend: (message: string) => void,
@@ -25,6 +30,7 @@
 
   const { addListener, removeListener } = useTunnelbroker();
   const peerToPeerMessageHandler = usePeerToPeerMessageHandler();
+  const handleOlmMessageToDevice = useHandleOlmMessageToDevice();
 
   const [messagesQueue, setMessagesQueue] = React.useState<
     Array<{
@@ -34,6 +40,27 @@
     }>,
   >([]);
   const [isProcessing, setProcessing] = React.useState(false);
+  const [processedInboundMessages, setProcessedInboundMessages] =
+    React.useState(false);
+
+  React.useEffect(() => {
+    void (async () => {
+      const { sqliteAPI } = getConfig();
+      const messages = await sqliteAPI.getAllInboundP2PMessages();
+
+      for (const message: InboundP2PMessage of messages) {
+        try {
+          await handleOlmMessageToDevice(
+            message.plaintext,
+            { deviceID: message.senderDeviceID, userID: message.senderUserID },
+            message.messageID,
+          );
+        } catch (e) {
+          console.log('Failed processing Olm P2P message:', e);
+        }
+      }
+    })();
+  }, [handleOlmMessageToDevice]);
 
   const tunnelbrokerMessageListener = React.useCallback(
     async (message: TunnelbrokerToDeviceMessage) => {
@@ -79,6 +106,33 @@
     [getSessionCounter, socketSend],
   );
 
+  const processPersistedInboundMessages = React.useCallback(async () => {
+    if (isProcessing || processedInboundMessages) {
+      return;
+    }
+    setProcessing(true);
+
+    try {
+      const { sqliteAPI } = getConfig();
+      const messages = await sqliteAPI.getAllInboundP2PMessages();
+
+      for (const message: InboundP2PMessage of messages) {
+        try {
+          await handleOlmMessageToDevice(
+            message.plaintext,
+            { deviceID: message.senderDeviceID, userID: message.senderUserID },
+            message.messageID,
+          );
+        } catch (e) {
+          console.log('Failed processing Olm P2P message:', e);
+        }
+      }
+    } finally {
+      setProcessedInboundMessages(true);
+      setProcessing(false);
+    }
+  }, [handleOlmMessageToDevice, isProcessing, processedInboundMessages]);
+
   const processMessage = React.useCallback(async () => {
     if (messagesQueue.length === 0 || isProcessing) {
       return;
@@ -130,10 +184,21 @@
   ]);
 
   React.useEffect(() => {
-    if (!isProcessing && messagesQueue.length > 0) {
+    if (isProcessing) {
+      return;
+    }
+    if (!processedInboundMessages) {
+      void processPersistedInboundMessages();
+    } else if (messagesQueue.length > 0) {
       void processMessage();
     }
-  }, [messagesQueue, isProcessing, processMessage]);
+  }, [
+    messagesQueue,
+    isProcessing,
+    processMessage,
+    processedInboundMessages,
+    processPersistedInboundMessages,
+  ]);
 
   React.useEffect(() => {
     addListener(tunnelbrokerMessageListener);
diff --git a/lib/tunnelbroker/use-peer-to-peer-message-handler.js b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
--- a/lib/tunnelbroker/use-peer-to-peer-message-handler.js
+++ b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
@@ -58,7 +58,11 @@
 });
 
 // handles `peerToPeerMessageTypes.ENCRYPTED_MESSAGE`
-function useHandleOlmMessageToDevice() {
+function useHandleOlmMessageToDevice(): (
+  decryptedMessageContent: string,
+  senderInfo: SenderInfo,
+  messageID: string,
+) => Promise<void> {
   const identityContext = React.useContext(IdentityClientContext);
   invariant(identityContext, 'Identity context should be set');
 
@@ -81,6 +85,8 @@
       senderInfo: SenderInfo,
       messageID: string,
     ) => {
+      const { sqliteAPI } = getConfig();
+
       const parsedMessageToDevice = JSON.parse(decryptedMessageContent);
 
       // Handle user-action messages
@@ -110,6 +116,7 @@
         await broadcastDeviceListUpdates(
           allPeerDevices.filter(deviceID => deviceID !== deviceIDToLogOut),
         );
+        await sqliteAPI.removeInboundP2PMessages([messageID]);
       } else if (
         userActionMessage.type === userActionsP2PMessageTypes.DM_OPERATION
       ) {
@@ -138,6 +145,7 @@
             type: removePeerUsersActionType,
             payload: { userIDs: [senderInfo.userID] },
           });
+          await sqliteAPI.removeInboundP2PMessages([messageID]);
         }
       } else {
         console.warn(
@@ -410,4 +418,4 @@
   );
 }
 
-export { usePeerToPeerMessageHandler };
+export { usePeerToPeerMessageHandler, useHandleOlmMessageToDevice };