diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h
index 33ebe0699fdf..a0eec42fac7b 100644
--- a/include/libcamera/internal/camera_manager.h
+++ b/include/libcamera/internal/camera_manager.h
@@ -19,13 +19,13 @@
 #include <libcamera/base/thread.h>
 #include <libcamera/base/thread_annotations.h>
 
-#include "libcamera/internal/ipa_manager.h"
 #include "libcamera/internal/process.h"
 
 namespace libcamera {
 
 class Camera;
 class DeviceEnumerator;
+class IPAManager;
 
 class CameraManager::Private : public Extensible::Private, public Thread
 {
@@ -38,6 +38,8 @@ public:
 	void addCamera(std::shared_ptr<Camera> camera) LIBCAMERA_TSA_EXCLUDES(mutex_);
 	void removeCamera(std::shared_ptr<Camera> camera) LIBCAMERA_TSA_EXCLUDES(mutex_);
 
+	IPAManager *ipaManager() const { return ipaManager_.get(); }
+
 protected:
 	void run() override;
 
@@ -61,7 +63,7 @@ private:
 
 	std::unique_ptr<DeviceEnumerator> enumerator_;
 
-	IPAManager ipaManager_;
+	std::unique_ptr<IPAManager> ipaManager_;
 	ProcessManager processManager_;
 };
 
diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h
index bf823563c91c..23db6b8a0221 100644
--- a/include/libcamera/internal/ipa_manager.h
+++ b/include/libcamera/internal/ipa_manager.h
@@ -15,6 +15,7 @@
 #include <libcamera/ipa/ipa_interface.h>
 #include <libcamera/ipa/ipa_module_info.h>
 
+#include "libcamera/internal/camera_manager.h"
 #include "libcamera/internal/ipa_module.h"
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/pub_key.h"
@@ -34,11 +35,13 @@ public:
 					    uint32_t minVersion,
 					    uint32_t maxVersion)
 	{
-		IPAModule *m = self_->module(pipe, minVersion, maxVersion);
+		CameraManager *cm = pipe->cameraManager();
+		IPAManager *self = cm->_d()->ipaManager();
+		IPAModule *m = self->module(pipe, minVersion, maxVersion);
 		if (!m)
 			return nullptr;
 
-		std::unique_ptr<T> proxy = std::make_unique<T>(m, !self_->isSignatureValid(m));
+		std::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m));
 		if (!proxy->isValid()) {
 			LOG(IPAManager, Error) << "Failed to load proxy";
 			return nullptr;
@@ -55,8 +58,6 @@ public:
 #endif
 
 private:
-	static IPAManager *self_;
-
 	void parseDir(const char *libDir, unsigned int maxDepth,
 		      std::vector<std::string> &files);
 	unsigned int addDir(const char *libDir, unsigned int maxDepth = 0);
diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
index c96944f4ecc4..bbfae557e023 100644
--- a/include/libcamera/internal/pipeline_handler.h
+++ b/include/libcamera/internal/pipeline_handler.h
@@ -70,6 +70,8 @@ public:
 
 	const char *name() const { return name_; }
 
+	CameraManager *cameraManager() const { return manager_; }
+
 protected:
 	void registerCamera(std::shared_ptr<Camera> camera);
 	void hotplugMediaDevice(MediaDevice *media);
diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
index 355f3adad68f..909a3cd70117 100644
--- a/src/libcamera/camera_manager.cpp
+++ b/src/libcamera/camera_manager.cpp
@@ -15,6 +15,7 @@
 
 #include "libcamera/internal/camera.h"
 #include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_manager.h"
 #include "libcamera/internal/pipeline_handler.h"
 
 /**
@@ -37,6 +38,7 @@ LOG_DEFINE_CATEGORY(Camera)
 CameraManager::Private::Private()
 	: initialized_(false)
 {
+	ipaManager_ = std::make_unique<IPAManager>();
 }
 
 int CameraManager::Private::start()
@@ -220,6 +222,13 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
 	o->cameraRemoved.emit(camera);
 }
 
+/**
+ * \fn CameraManager::Private::ipaManager() const
+ * \brief Retrieve the IPAManager
+ * \context This function is \threadsafe.
+ * \return The IPAManager for this CameraManager
+ */
+
 /**
  * \class CameraManager
  * \brief Provide access and manage all cameras in the system
diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp
index 7a4515d90d7b..be1bb704c6cd 100644
--- a/src/libcamera/ipa_manager.cpp
+++ b/src/libcamera/ipa_manager.cpp
@@ -95,8 +95,6 @@ LOG_DEFINE_CATEGORY(IPAManager)
  * IPC.
  */
 
-IPAManager *IPAManager::self_ = nullptr;
-
 /**
  * \brief Construct an IPAManager instance
  *
@@ -105,10 +103,6 @@ IPAManager *IPAManager::self_ = nullptr;
  */
 IPAManager::IPAManager()
 {
-	if (self_)
-		LOG(IPAManager, Fatal)
-			<< "Multiple IPAManager objects are not allowed";
-
 #if HAVE_IPA_PUBKEY
 	if (!pubKey_.isValid())
 		LOG(IPAManager, Warning) << "Public key not valid";
@@ -153,16 +147,12 @@ IPAManager::IPAManager()
 	if (!ipaCount)
 		LOG(IPAManager, Warning)
 			<< "No IPA found in '" IPA_MODULE_DIR "'";
-
-	self_ = this;
 }
 
 IPAManager::~IPAManager()
 {
 	for (IPAModule *module : modules_)
 		delete module;
-
-	self_ = nullptr;
 }
 
 /**
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index 9c74c6cfda70..15783fdc7447 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -719,6 +719,13 @@ void PipelineHandler::disconnect()
  * \return The pipeline handler name
  */
 
+/**
+ * \fn PipelineHandler::cameraManager() const
+ * \brief Retrieve the CameraManager that this pipeline handler belongs to
+ * \context This function is \threadsafe.
+ * \return The CameraManager for this pipeline handler
+ */
+
 /**
  * \class PipelineHandlerFactoryBase
  * \brief Base class for pipeline handler factories
diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp
index 051ef96e7ed2..af52eed72ec8 100644
--- a/test/ipa/ipa_interface_test.cpp
+++ b/test/ipa/ipa_interface_test.cpp
@@ -19,11 +19,11 @@
 #include <libcamera/base/thread.h>
 #include <libcamera/base/timer.h>
 
+#include "libcamera/internal/camera_manager.h"
 #include "libcamera/internal/device_enumerator.h"
 #include "libcamera/internal/ipa_manager.h"
 #include "libcamera/internal/ipa_module.h"
 #include "libcamera/internal/pipeline_handler.h"
-#include "libcamera/internal/process.h"
 
 #include "test.h"
 
@@ -43,20 +43,20 @@ public:
 	{
 		delete notifier_;
 		ipa_.reset();
-		ipaManager_.reset();
+		cameraManager_.reset();
 	}
 
 protected:
 	int init() override
 	{
-		ipaManager_ = make_unique<IPAManager>();
+		cameraManager_ = make_unique<CameraManager>();
 
 		/* Create a pipeline handler for vimc. */
 		const std::vector<PipelineHandlerFactoryBase *> &factories =
 			PipelineHandlerFactoryBase::factories();
 		for (const PipelineHandlerFactoryBase *factory : factories) {
 			if (factory->name() == "PipelineHandlerVimc") {
-				pipe_ = factory->create(nullptr);
+				pipe_ = factory->create(cameraManager_.get());
 				break;
 			}
 		}
@@ -170,11 +170,9 @@ private:
 		}
 	}
 
-	ProcessManager processManager_;
-
 	std::shared_ptr<PipelineHandler> pipe_;
 	std::unique_ptr<ipa::vimc::IPAProxyVimc> ipa_;
-	std::unique_ptr<IPAManager> ipaManager_;
+	std::unique_ptr<CameraManager> cameraManager_;
 	enum ipa::vimc::IPAOperationCode trace_;
 	EventNotifier *notifier_;
 	int fd_;
