diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass
index 15a91e90e2..dfbd2961b3 100644
--- a/meta/classes-recipe/create-spdx-image-3.0.bbclass
+++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass
@@ -6,6 +6,7 @@
 # SPDX image tasks
 
 SPDX_ROOTFS_PACKAGES = "${SPDXDIR}/rootfs-packages.json"
+SPDX_ROOTFS_REMOVED_PACKAGES = "${SPDXDIR}/rootfs-removed-packages.json"
 SPDXIMAGEDEPLOYDIR = "${SPDXDIR}/image-deploy"
 SPDXROOTFSDEPLOY = "${SPDXDIR}/rootfs-deploy"
 
@@ -15,14 +16,20 @@ python spdx_collect_rootfs_packages() {
     from oe.rootfs import image_list_installed_packages
 
     root_packages_file = Path(d.getVar("SPDX_ROOTFS_PACKAGES"))
+    root_removed_packages_file = Path(d.getVar("SPDX_ROOTFS_REMOVED_PACKAGES"))
 
     packages = image_list_installed_packages(d)
     if not packages:
         packages = {}
 
+    removed_packages = (d.getVar("ROOTFS_REMOVED_PACKAGES") or "").split()
+
     root_packages_file.parent.mkdir(parents=True, exist_ok=True)
     with root_packages_file.open("w") as f:
         json.dump(packages, f)
+
+    with root_removed_packages_file.open("w") as f:
+        json.dump(removed_packages, f)
 }
 ROOTFS_POSTUNINSTALL_COMMAND =+ "spdx_collect_rootfs_packages"
 
diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py
index 5eee48f587..b8830596ed 100644
--- a/meta/lib/oe/rootfs.py
+++ b/meta/lib/oe/rootfs.py
@@ -261,10 +261,13 @@ class Rootfs(object, metaclass=ABCMeta):
 
 
     def _uninstall_unneeded(self):
+        removed_pkgs = set()
+
         # Remove the run-postinsts package if no delayed postinsts are found
         delayed_postinsts = self._get_delayed_postinsts()
         if delayed_postinsts is None:
             if os.path.exists(self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/init.d/run-postinsts")) or os.path.exists(self.d.expand("${IMAGE_ROOTFS}${systemd_system_unitdir}/run-postinsts.service")):
+                removed_pkgs.add("run-postinsts")
                 self.pm.remove(["run-postinsts"])
 
         image_rorfs = bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs",
@@ -285,6 +288,7 @@ class Rootfs(object, metaclass=ABCMeta):
             # to be uninstalled or to be managed correctly otherwise.
             provider = self.d.getVar("VIRTUAL-RUNTIME_update-alternatives")
             pkgs_to_remove = sorted([pkg for pkg in pkgs_installed if pkg in unneeded_pkgs], key=lambda x: x == provider)
+            removed_pkgs.update(pkgs_to_remove)
 
             # update-alternatives provider is removed in its own remove()
             # call because all package managers do not guarantee the packages
@@ -296,6 +300,8 @@ class Rootfs(object, metaclass=ABCMeta):
             if len(pkgs_to_remove) > 0:
                 self.pm.remove([pkgs_to_remove[-1]], False)
 
+        self.d.setVar("ROOTFS_REMOVED_PACKAGES", " ".join(sorted(removed_pkgs)))
+
         if delayed_postinsts:
             self._save_postinsts()
             if image_rorfs:
diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py
index b379ff947c..4fa32266fa 100644
--- a/meta/lib/oe/sbom30.py
+++ b/meta/lib/oe/sbom30.py
@@ -1122,7 +1122,7 @@ def find_by_spdxid(d, spdxid, *, required=False):
     return find_jsonld(d, *jsonld_hash_path(hash_id(spdxid)), required=required)
 
 
-def create_sbom(d, name, root_elements, add_objectsets=[]):
+def create_sbom(d, name, root_elements, add_objectsets=[], removed_packages=[]):
     objset = ObjectSet.new_objset(d, name)
 
     sbom = objset.add(
@@ -1142,6 +1142,12 @@ def create_sbom(d, name, root_elements, add_objectsets=[]):
             + "\n  ".join(sorted(list(missing_spdxids)))
         )
 
+    if removed_packages:
+        for pkg in objset.foreach_type(oe.spdx30.software_Package):
+            if pkg.name in removed_packages and pkg.software_primaryPurpose == oe.spdx30.software_SoftwarePurpose.install:
+                pkg.software_primaryPurpose = oe.spdx30.software_SoftwarePurpose.other
+                bb.note("Reclassified removed package %s SPDX entry from install to other" % pkg.name)
+
     # Filter out internal extensions from final SBoMs
     objset.remove_internal_extensions()
 
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
index 7cc46d579b..18c68f47de 100644
--- a/meta/lib/oe/spdx30_tasks.py
+++ b/meta/lib/oe/spdx30_tasks.py
@@ -1532,6 +1532,7 @@ def create_image_sbom_spdx(d):
     image_link_name = d.getVar("IMAGE_LINK_NAME")
     imgdeploydir = Path(d.getVar("SPDXIMAGEDEPLOYDIR"))
     machine = d.getVar("MACHINE")
+    root_removed_packages_file = Path(d.getVar("SPDX_ROOTFS_REMOVED_PACKAGES"))
 
     spdx_path = imgdeploydir / (image_name + ".spdx.json")
 
@@ -1553,7 +1554,18 @@ def create_image_sbom_spdx(d):
     for o in image_objset.foreach_root(oe.spdx30.software_File):
         root_elements.append(oe.sbom30.get_element_link_id(o))
 
-    objset, sbom = oe.sbom30.create_sbom(d, image_name, root_elements)
+    try:
+        with root_removed_packages_file.open("r") as f:
+            removed_packages = json.load(f)
+    except FileNotFoundError:
+        removed_packages = []
+
+    objset, sbom = oe.sbom30.create_sbom(
+        d,
+        image_name,
+        root_elements,
+        removed_packages=removed_packages,
+    )
 
     # Set supplier on root elements if SPDX_IMAGE_SUPPLIER is defined
     supplier = objset.new_agent("SPDX_IMAGE_SUPPLIER", add=False)
