diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h
index 7bb6158c79f275812d423dd9b37e1ebdf75b339e..659fd507b64ee3100c0f9281c8f1adfc745395df 100644
--- a/include/opendht/dhtrunner.h
+++ b/include/opendht/dhtrunner.h
@@ -63,6 +63,12 @@ public:
         bool peer_publish;
     };
 
+    struct Context {
+        std::unique_ptr<Logger> logger {};
+        std::shared_ptr<PeerDiscovery> peerDiscovery {};
+        Context() {}
+    };
+
     DhtRunner();
     virtual ~DhtRunner();
 
@@ -375,7 +381,7 @@ public:
             /*.peer_publish = */true,
         });
     }
-    void run(in_port_t port, const Config& config);
+    void run(in_port_t port, const Config& config, Context&& context = {});
 
     /**
      * @param local4: Local IPv4 address and port to bind. Can be null.
@@ -385,12 +391,12 @@ public:
      * @param threaded: If false, loop() must be called periodically. Otherwise a thread is launched.
      * @param cb: Optional callback to receive general state information.
      */
-    void run(const SockAddr& local4, const SockAddr& local6, const Config& config);
+    void run(const SockAddr& local4, const SockAddr& local6, const Config& config, Context&& context = {});
 
     /**
      * Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
      */
-    void run(const char* ip4, const char* ip6, const char* service, const Config& config);
+    void run(const char* ip4, const char* ip6, const char* service, const Config& config, Context&& context = {});
 
     void setOnStatusChanged(StatusCallback&& cb) {
         statusCb = std::move(cb);
@@ -424,7 +430,7 @@ public:
      */
     void join();
 
-    PeerDiscovery* getPeerDiscovery() { return peerDiscovery_.get(); };
+    std::shared_ptr<PeerDiscovery> getPeerDiscovery() const { return peerDiscovery_; };
 
     void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "");
 
@@ -541,7 +547,7 @@ private:
     std::string pushToken_;
 
     /** PeerDiscovery Parameters */
-    std::unique_ptr<PeerDiscovery> peerDiscovery_;
+    std::shared_ptr<PeerDiscovery> peerDiscovery_;
 };
 
 }
diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp
index f760386af2a3326896e672497d64eff2faa6fed3..e8f7f73d653491a7ff36c04a186733d2e486037d 100644
--- a/src/dhtrunner.cpp
+++ b/src/dhtrunner.cpp
@@ -79,7 +79,7 @@ DhtRunner::~DhtRunner()
 }
 
 void
-DhtRunner::run(in_port_t port, const DhtRunner::Config& config)
+DhtRunner::run(in_port_t port, const DhtRunner::Config& config, Context&& context)
 {
     SockAddr sin4;
     sin4.setFamily(AF_INET);
@@ -87,20 +87,20 @@ DhtRunner::run(in_port_t port, const DhtRunner::Config& config)
     SockAddr sin6;
     sin6.setFamily(AF_INET6);
     sin6.setPort(port);
-    run(sin4, sin6, config);
+    run(sin4, sin6, config, std::move(context));
 }
 
 void
-DhtRunner::run(const char* ip4, const char* ip6, const char* service, const DhtRunner::Config& config)
+DhtRunner::run(const char* ip4, const char* ip6, const char* service, const DhtRunner::Config& config, Context&& context)
 {
     auto res4 = SockAddr::resolve(ip4, service);
     auto res6 = SockAddr::resolve(ip6, service);
     run(res4.empty() ? SockAddr() : res4.front(),
-        res6.empty() ? SockAddr() : res6.front(), config);
+        res6.empty() ? SockAddr() : res6.front(), config, std::move(context));
 }
 
 void
-DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const DhtRunner::Config& config)
+DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const DhtRunner::Config& config, Context&& context)
 {
     if (running)
         return;
@@ -114,6 +114,13 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const DhtRunner::
 #endif
     enableProxy(not config.proxy_server.empty());
 
+    if (context.logger) {
+        if (dht_)
+            dht_->setLoggers(context.logger->ERR, context.logger->WARN, context.logger->DBG);
+        if (dht_via_proxy_)
+            dht_via_proxy_->setLoggers(context.logger->ERR, context.logger->WARN, context.logger->DBG);
+    }
+
     running = true;
     if (not config.threaded)
         return;
@@ -153,7 +160,10 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const DhtRunner::
     });
 
     if (config.peer_discovery or config.peer_publish) {
-        peerDiscovery_.reset(new PeerDiscovery(PEER_DISCOVERY_PORT));
+        if (context.peerDiscovery)
+            peerDiscovery_ = std::move(context.peerDiscovery);
+        else
+            peerDiscovery_.reset(new PeerDiscovery(PEER_DISCOVERY_PORT));
     }
 
     auto netId = config.dht_config.node_config.network;
@@ -220,7 +230,9 @@ DhtRunner::join()
     if (rcv_thread.joinable())
         rcv_thread.join();
 
-    if (peerDiscovery_) peerDiscovery_->join();
+    if (peerDiscovery_) {
+        peerDiscovery_->join();
+    }
 
     {
         std::lock_guard<std::mutex> lck(storage_mtx);