diff --git a/epicardium/ble/bondings.c b/epicardium/ble/bondings.c
index 54a774b7c26bd2c03de260e11f7ef20b4d8a2cec..3f43ebde45b48fdcdf7039ca0fe0495e2219b70f 100644
--- a/epicardium/ble/bondings.c
+++ b/epicardium/ble/bondings.c
@@ -20,7 +20,7 @@
 /* card10:
  * copied from: lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/apps/app/common/app_db.c
  *
- * Reason: we need to implement persistent storage for pairings
+ * Reason: we need to implement persistent storage for bondings
  */
 /* clang-format off */
 /* clang-formet turned off for easier diffing against orginal file */
@@ -41,6 +41,35 @@
 #include <string.h>
 #include <stdio.h>
 
+/**************************************************************************************************
+  Macros
+**************************************************************************************************/
+
+/* App DB NVM record parameter indicies from upstream:
+ * https://github.com/packetcraft-inc/stacks/blob/master/ble-profiles/sources/af/common/app_db.c#L46
+ */
+#define APP_DB_NVM_IN_USE_ID                  0
+#define APP_DB_NVM_PEER_ADDR_ID               1
+#define APP_DB_NVM_ADDR_TYPE_ID               2
+#define APP_DB_NVM_PEER_IRK_ID                3
+#define APP_DB_NVM_PEER_CSRK_ID               4
+#define APP_DB_NVM_KV_MASK_ID                 5
+#define APP_DB_NVM_VALID_ID                   6
+#define APP_DB_NVM_PEER_RAPO_ID               7
+#define APP_DB_NVM_LOCAL_LTK_ID               8
+#define APP_DB_NVM_LOCAL_SEC_LVL_ID           9
+#define APP_DB_NVM_PEER_ADDR_RES_ID           10
+#define APP_DB_NVM_PEER_LTK_ID                11
+#define APP_DB_NVM_PEER_SEC_LVL_ID            12
+#define APP_DB_NVM_CCC_TBL_ID                 13
+#define APP_DB_NVM_PEER_SIGN_CTR_ID           14
+#define APP_DB_NVM_CAS_ID                     15
+#define APP_DB_NVM_CSF_ID                     16
+#define APP_DB_NVM_CACHE_HASH_ID              17
+#define APP_DB_NVM_HASH_ID                    18
+#define APP_DB_NVM_HDL_LIST_ID                19
+#define APP_DB_NVM_DISC_STATUS_ID             20
+
 /**************************************************************************************************
   Data Types
 **************************************************************************************************/
@@ -88,6 +117,165 @@ static appDbRec_t records[APP_DB_NUM_RECS];
 /*! When all records are allocated use this index to determine which to overwrite */
 static appDbRec_t *pAppDbNewRec = records;
 
+/* clang-format on */
+/* Translate a pointer to a record into the filename to be used for it. */
+static int record_to_filename(appDbRec_t *record, char *buf, size_t buf_size)
+{
+	int id  = record - records;
+	int ret = snprintf(buf, buf_size, "pairings/pairing%d.bin", id + 1);
+	if (ret >= (int)buf_size) {
+		ret = -1;
+	}
+	return ret;
+}
+
+/* Write a TLV to a file. */
+static int write_tlv(int fd, uint32_t t, uint32_t l, void *v)
+{
+	int ret;
+
+	ret = epic_file_write(fd, &t, sizeof(t));
+	if (ret != sizeof(t))
+		return ret;
+
+	ret = epic_file_write(fd, &l, sizeof(l));
+	if (ret != sizeof(l))
+		return ret;
+
+	ret = epic_file_write(fd, v, l);
+	if (ret != (int)l)
+		return ret;
+
+	return 0;
+}
+
+/* Read a TLV from a file.
+ *
+ * Super naive implementation assuming that the next TLV is
+ * the expected one. */
+static int read_tlv(int fd, uint32_t t, uint32_t l, void *v)
+{
+	int ret;
+
+	uint32_t t_r;
+	ret = epic_file_read(fd, &t_r, sizeof(t_r));
+	if (ret != sizeof(t))
+		return ret;
+	if (t != t_r)
+		return -ENOENT;
+
+	uint32_t l_r;
+	ret = epic_file_read(fd, &l_r, sizeof(l_r));
+	if (ret != sizeof(l_r))
+		return ret;
+	if (l_r > l)
+		return -EINVAL;
+
+	memset(v, 0, l);
+
+	ret = epic_file_read(fd, v, l_r);
+	if (ret != (int)l_r)
+		return ret;
+
+	return 0;
+}
+
+static int write_bond_to_file(appDbRec_t *r, char *filename)
+{
+	int fd = epic_file_open(filename, "w");
+	int ret;
+	if (fd < 0) {
+		return fd;
+	}
+
+	static const uint8_t version = 1;
+	ret = epic_file_write(fd, &version, sizeof(version));
+	if (ret != sizeof(version))
+		goto out;
+
+#define write_element(t, x)                                                    \
+	if ((ret = write_tlv(fd, t, sizeof(r->x), &r->x)))                     \
+		goto out;
+
+	write_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
+	write_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
+	write_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
+	write_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
+	write_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
+	/* peerAddedToRl not persisted by upstream */
+	/* write_element(, peerAddedToRl); */
+	write_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
+	write_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
+	write_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
+	write_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
+	write_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
+	write_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
+	write_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
+	write_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
+	write_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
+	write_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
+	write_element(APP_DB_NVM_VALID_ID, valid);
+
+#undef write_element
+
+out:
+	epic_file_close(fd);
+	return ret;
+}
+
+static int read_bond_from_file(appDbRec_t *r, char *filename)
+{
+	int fd = epic_file_open(filename, "r");
+	if (fd < 0) {
+		return fd;
+	}
+
+	uint8_t version;
+	int ret = epic_file_read(fd, &version, sizeof(version));
+	if (ret != sizeof(version)) {
+		goto out;
+	}
+	if (version != 1) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+#define read_element(t, x)                                                     \
+	if ((ret = read_tlv(fd, t, sizeof(r->x), &r->x)))                      \
+		goto out;
+
+	read_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
+	read_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
+	read_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
+	read_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
+	read_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
+	/* peerAddedToRl not persisted by upstream */
+	/* read_element(, peerAddedToRl); */
+	read_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
+	read_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
+	read_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
+	read_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
+	read_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
+	read_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
+	read_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
+	read_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
+	read_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
+	read_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
+	read_element(APP_DB_NVM_VALID_ID, valid);
+
+#undef read_element
+
+	r->inUse = true;
+out:
+	epic_file_close(fd);
+	return ret;
+}
+
+static int delete_bond(char *filename)
+{
+	return epic_file_unlink(filename);
+}
+
 /*************************************************************************************************/
 /*!
  *  \brief  Initialize the device database.
@@ -97,28 +285,27 @@ static appDbRec_t *pAppDbNewRec = records;
 /*************************************************************************************************/
 void AppDbInit(void)
 {
-  int fd = epic_file_open("pairings.bin", "r");
-
-  if(fd >= 0) {
-    if(epic_file_read(fd, records, sizeof(records)) != sizeof(records)) {
-        memset(records, 0, sizeof(records));
-    }
-    epic_file_close(fd);
-  }
-}
-
-static void AppDbStore(void)
-{
-  LOG_INFO("bondings", "writing to persistent storage");
-
-  int fd = epic_file_open("pairings.bin", "w");
-  if(fd >= 0) {
-    if(epic_file_write(fd, records, sizeof(records)) != sizeof(records)) {
-    }
-    epic_file_close(fd);
-  }
+	memset(&records, 0, sizeof(records));
+
+	char filename[32];
+	for (int i = 0; i < APP_DB_NUM_RECS; i++) {
+		record_to_filename(&records[i], filename, sizeof(filename));
+		int ret = read_bond_from_file(&records[i], filename);
+		if (ret < 0) {
+			if (ret != -ENOENT) {
+				LOG_WARN(
+					"bondings",
+					"Reading pairing '%s' failed: %d",
+					filename,
+					ret
+				);
+			}
+			memset(&records[i], 0, sizeof(records[i]));
+		}
+	}
 }
 
+/* clang-format off */
 /*************************************************************************************************/
 /*!
  *  \brief  Create a new device database record.
@@ -229,6 +416,9 @@ appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
 void AppDbDeleteRecord(appDbHdl_t hdl)
 {
   ((appDbRec_t *) hdl)->inUse = FALSE;
+  char filename[32];
+  record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename));
+  delete_bond(filename);
 }
 
 /*************************************************************************************************/
@@ -246,7 +436,11 @@ void AppDbValidateRecord(appDbHdl_t hdl, uint8_t keyMask)
 {
   ((appDbRec_t *) hdl)->valid = TRUE;
   ((appDbRec_t *) hdl)->keyValidMask = keyMask;
-  AppDbStore();
+  char filename[32];
+  record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename));
+  /* Directory might exist already. Call will fail silently in that case. */
+  epic_file_mkdir("pairings");
+  write_bond_to_file((appDbRec_t *) hdl, filename);
 }
 
 /*************************************************************************************************/
@@ -514,6 +708,10 @@ void AppDbSetCccTblValue(appDbHdl_t hdl, uint16_t idx, uint16_t value)
   WSF_ASSERT(idx < APP_DB_NUM_CCCD);
 
   ((appDbRec_t *) hdl)->cccTbl[idx] = value;
+
+  char filename[32];
+  record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename));
+  write_bond_to_file((appDbRec_t *) hdl, filename);
 }
 
 /*************************************************************************************************/