diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h
index 84aac499ea13..cdf009a9c626 100644
--- a/include/libcamera/internal/camera_manager.h
+++ b/include/libcamera/internal/camera_manager.h
@@ -35,8 +35,7 @@ public:
 	Private();
 
 	int start();
-	void addCamera(std::shared_ptr<Camera> camera,
-		       const std::vector<dev_t> &devnums) LIBCAMERA_TSA_EXCLUDES(mutex_);
+	void addCamera(std::shared_ptr<Camera> camera) LIBCAMERA_TSA_EXCLUDES(mutex_);
 	void removeCamera(std::shared_ptr<Camera> camera) LIBCAMERA_TSA_EXCLUDES(mutex_);
 
 	/*
diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
index cafd7bce574e..31d45c42fde0 100644
--- a/src/libcamera/camera_manager.cpp
+++ b/src/libcamera/camera_manager.cpp
@@ -11,7 +11,9 @@
 #include <libcamera/base/utils.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/property_ids.h>
 
+#include "libcamera/internal/camera.h"
 #include "libcamera/internal/device_enumerator.h"
 #include "libcamera/internal/pipeline_handler.h"
 
@@ -151,19 +153,17 @@ void CameraManager::Private::cleanup()
 /**
  * \brief Add a camera to the camera manager
  * \param[in] camera The camera to be added
- * \param[in] devnums The device numbers to associate with \a camera
  *
  * This function is called by pipeline handlers to register the cameras they
  * handle with the camera manager. Registered cameras are immediately made
  * available to the system.
  *
- * \a devnums are used by the V4L2 compatibility layer to map V4L2 device nodes
- * to Camera instances.
+ * Device numbers from the Devices property are used by the V4L2 compatibility
+ * layer to map V4L2 device nodes to Camera instances.
  *
  * \context This function shall be called from the CameraManager thread.
  */
-void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera,
-				       const std::vector<dev_t> &devnums)
+void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
 {
 	ASSERT(Thread::current() == this);
 
@@ -178,6 +178,10 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera,
 		}
 	}
 
+	auto devnums = camera->properties()
+			       .get(properties::SystemDevices)
+			       .value_or(Span<int64_t>{});
+
 	cameras_.push_back(std::move(camera));
 
 	unsigned int index = cameras_.size() - 1;
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index 49092ea88a58..9c74c6cfda70 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -17,6 +17,7 @@
 
 #include <libcamera/camera.h>
 #include <libcamera/framebuffer.h>
+#include <libcamera/property_ids.h>
 
 #include "libcamera/internal/camera.h"
 #include "libcamera/internal/camera_manager.h"
@@ -612,7 +613,7 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
 	 * Walk the entity list and map the devnums of all capture video nodes
 	 * to the camera.
 	 */
-	std::vector<dev_t> devnums;
+	std::vector<int64_t> devnums;
 	for (const std::shared_ptr<MediaDevice> &media : mediaDevices_) {
 		for (const MediaEntity *entity : media->entities()) {
 			if (entity->pads().size() == 1 &&
@@ -624,7 +625,14 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
 		}
 	}
 
-	manager_->_d()->addCamera(std::move(camera), devnums);
+	/*
+	 * Store the associated devices as a property of the camera to allow
+	 * systems to identify which devices are managed by libcamera.
+	 */
+	Camera::Private *data = camera->_d();
+	data->properties_.set(properties::SystemDevices, devnums);
+
+	manager_->_d()->addCamera(std::move(camera));
 }
 
 /**
