diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index 4d1a4a9f52ec0fac..132225a37dcd1ef4 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -70,12 +70,14 @@ class Camera final : public std::enable_shared_from_this<Camera>
 {
 public:
 	static std::shared_ptr<Camera> create(PipelineHandler *pipe,
+					      const std::string &id,
 					      const std::string &name,
 					      const std::set<Stream *> &streams);
 
 	Camera(const Camera &) = delete;
 	Camera &operator=(const Camera &) = delete;
 
+	const std::string &id() const;
 	const std::string &name() const;
 
 	Signal<Request *, FrameBuffer *> bufferCompleted;
@@ -99,8 +101,8 @@ public:
 	int stop();
 
 private:
-	Camera(PipelineHandler *pipe, const std::string &name,
-	       const std::set<Stream *> &streams);
+	Camera(PipelineHandler *pipe, const std::string &id,
+	       const std::string &name, const std::set<Stream *> &streams);
 	~Camera();
 
 	class Private;
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index 69a1b4428e3f4eca..638d15e0da5e1ca9 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -265,8 +265,8 @@ public:
 		CameraRunning,
 	};
 
-	Private(PipelineHandler *pipe, const std::string &name,
-		const std::set<Stream *> &streams);
+	Private(PipelineHandler *pipe, const std::string &id,
+		const std::string &name, const std::set<Stream *> &streams);
 	~Private();
 
 	int isAccessAllowed(State state, bool allowDisconnected = false) const;
@@ -277,6 +277,7 @@ public:
 	void setState(State state);
 
 	std::shared_ptr<PipelineHandler> pipe_;
+	std::string id_;
 	std::string name_;
 	std::set<Stream *> streams_;
 	std::set<Stream *> activeStreams_;
@@ -286,10 +287,11 @@ private:
 	std::atomic<State> state_;
 };
 
-Camera::Private::Private(PipelineHandler *pipe, const std::string &name,
+Camera::Private::Private(PipelineHandler *pipe, const std::string &id,
+			 const std::string &name,
 			 const std::set<Stream *> &streams)
-	: pipe_(pipe->shared_from_this()), name_(name), streams_(streams),
-	  disconnected_(false), state_(CameraAvailable)
+	: pipe_(pipe->shared_from_this()), id_(id), name_(name),
+	  streams_(streams), disconnected_(false), state_(CameraAvailable)
 {
 }
 
@@ -450,14 +452,16 @@ void Camera::Private::setState(State state)
 /**
  * \brief Create a camera instance
  * \param[in] pipe The pipeline handler responsible for the camera device
+ * \param[in] id The ID of the camera device
  * \param[in] name The name of the camera device
  * \param[in] streams Array of streams the camera provides
  *
- * The caller is responsible for guaranteeing unicity of the camera name.
+ * The caller is responsible for guaranteeing unicity of the camera ID.
  *
  * \return A shared pointer to the newly created camera object
  */
 std::shared_ptr<Camera> Camera::create(PipelineHandler *pipe,
+				       const std::string &id,
 				       const std::string &name,
 				       const std::set<Stream *> &streams)
 {
@@ -468,7 +472,7 @@ std::shared_ptr<Camera> Camera::create(PipelineHandler *pipe,
 		}
 	};
 
-	Camera *camera = new Camera(pipe, name, streams);
+	Camera *camera = new Camera(pipe, id, name, streams);
 
 	return std::shared_ptr<Camera>(camera, Deleter());
 }
@@ -483,6 +487,16 @@ const std::string &Camera::name() const
 	return p_->name_;
 }
 
+/**
+ * \brief Retrieve the camera ID
+ * \context This function is \threadsafe.
+ * \return Unique ID of the camera device
+ */
+const std::string &Camera::id() const
+{
+	return p_->id_;
+}
+
 /**
  * \var Camera::bufferCompleted
  * \brief Signal emitted when a buffer for a request queued to the camera has
@@ -506,9 +520,9 @@ const std::string &Camera::name() const
  * application API calls by returning errors immediately.
  */
 
-Camera::Camera(PipelineHandler *pipe, const std::string &name,
-	       const std::set<Stream *> &streams)
-	: p_(new Private(pipe, name, streams))
+Camera::Camera(PipelineHandler *pipe, const std::string &id,
+	       const std::string &name, const std::set<Stream *> &streams)
+	: p_(new Private(pipe, id, name, streams))
 {
 }
 
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index eb00eecfd10a89e4..09bab78b999b8b52 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -815,6 +815,7 @@ int PipelineHandlerIPU3::registerCameras()
 		/* Create and register the Camera instance. */
 		std::string cameraName = cio2->sensor()->entity()->name();
 		std::shared_ptr<Camera> camera = Camera::create(this,
+								cio2->sensor()->id(),
 								cameraName,
 								streams);
 
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index bf1c77144f855df9..487dc819daa1d0ec 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -973,7 +973,10 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
 	streams.insert(&data->isp_[Isp::Stats]);
 
 	/* Create and register the camera. */
-	std::shared_ptr<Camera> camera = Camera::create(this, data->sensor_->model(), streams);
+	std::shared_ptr<Camera> camera = Camera::create(this,
+							data->sensor_->id(),
+							data->sensor_->model(),
+							streams);
 	registerCamera(std::move(camera), std::move(data));
 
 	return true;
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 52a0d862417cc4ec..6f3699fe1a53eeaf 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -971,7 +971,8 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
 
 	std::set<Stream *> streams{ &data->stream_ };
 	std::shared_ptr<Camera> camera =
-		Camera::create(this, sensor->name(), streams);
+		Camera::create(this, data->sensor_->id(), sensor->name(),
+			       streams);
 	registerCamera(std::move(camera), std::move(data));
 
 	return 0;
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 28d367883323d855..3c27cc28f672e020 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -800,7 +800,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
 			continue;
 
 		std::shared_ptr<Camera> camera =
-			Camera::create(this, data->sensor_->entity()->name(),
+			Camera::create(this, data->sensor_->id(),
+				       data->sensor_->entity()->name(),
 				       data->streams());
 		registerCamera(std::move(camera), std::move(data));
 	}
diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 47f383d1551d5193..4484b70c6ec5f0ca 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -406,8 +406,13 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)
 		return false;
 
 	/* Create and register the camera. */
+	std::string id = data->video_->devicePath();
+	const std::string dropStr = "/sys/devices/";
+	if (id.find(dropStr) == 0)
+		id.erase(0, dropStr.length());
+
 	std::set<Stream *> streams{ &data->stream_ };
-	std::shared_ptr<Camera> camera = Camera::create(this, media->model(), streams);
+	std::shared_ptr<Camera> camera = Camera::create(this, id, media->model(), streams);
 	registerCamera(std::move(camera), std::move(data));
 
 	/* Enable hot-unplug notifications. */
diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
index 4f461b928514022d..38656d28b357dd1b 100644
--- a/src/libcamera/pipeline/vimc/vimc.cpp
+++ b/src/libcamera/pipeline/vimc/vimc.cpp
@@ -434,7 +434,8 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
 	/* Create and register the camera. */
 	std::string name{ "VIMC " + data->sensor_->model() };
 	std::set<Stream *> streams{ &data->stream_ };
-	std::shared_ptr<Camera> camera = Camera::create(this, name, streams);
+	std::shared_ptr<Camera> camera = Camera::create(this, data->sensor_->id(),
+							name, streams);
 	registerCamera(std::move(camera), std::move(data));
 
 	return true;
