diff --git a/configure.ac b/configure.ac
index 53bb95210290c42eaf1d0cb700098a466c8a0b48..bc45102e071b604c6eb61ce9792d7b2bffbabb91 100644
--- a/configure.ac
+++ b/configure.ac
@@ -209,6 +209,8 @@ AS_IF([test "x$enable_shared" == "xyes"], [
 ])
 AC_MSG_RESULT([$RING_SHARED])
 
+CPPFLAGS="${CPPFLAGS} -DASIO_STANDALONE"
+
 dnl
 dnl  Check for the contrib directory
 dnl
diff --git a/src/manager.cpp b/src/manager.cpp
index 169b7d1ba268314e789a649fabc3f20e3f8b49ab..dc569b02c21536c9434aeab751cfb4757c922572 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -83,6 +83,9 @@ using random_device = dht::crypto::random_device;
 
 #include <opendht/thread_pool.h>
 
+#include <asio/io_context.hpp>
+#include <asio/executor_work_guard.hpp>
+
 #ifndef WIN32
 #include <sys/time.h>
 #include <sys/resource.h>
@@ -331,6 +334,9 @@ struct Manager::ManagerPimpl
 
     Manager& base_; // pimpl back-pointer
 
+    std::shared_ptr<asio::io_context> ioContext_;
+    std::thread ioContextRunner_;
+
     /** Main scheduler */
     ScheduledExecutor scheduler_;
 
@@ -408,6 +414,7 @@ struct Manager::ManagerPimpl
 
 Manager::ManagerPimpl::ManagerPimpl(Manager& base)
     : base_(base)
+    , ioContext_(std::make_shared<asio::io_context>())
     , toneCtrl_(base.preferences)
     , currentCallMutex_()
     , dtmfKey_()
@@ -431,6 +438,15 @@ Manager::ManagerPimpl::ManagerPimpl(Manager& base)
     rand_.seed(seed);
 
     jami::libav_utils::ring_avcodec_init();
+
+    ioContextRunner_ = std::thread([context = ioContext_](){
+        try {
+            auto work = asio::make_work_guard(*context);
+            context->run();
+        } catch (const std::exception& ex) {
+            JAMI_ERR("Unexpected io_context thread exception: %s", ex.what());
+        }
+    });
 }
 
 bool
@@ -836,6 +852,13 @@ Manager::finish() noexcept
         dht::ThreadPool::computation().join();
 
         pj_shutdown();
+
+        if (!pimpl_->ioContext_->stopped()){
+            pimpl_->ioContext_->reset(); // allow to finish
+            pimpl_->ioContext_->stop();  // make thread stop
+        }
+        if (pimpl_->ioContextRunner_.joinable())
+            pimpl_->ioContextRunner_.join();
     } catch (const VoipLinkException &err) {
         JAMI_ERR("%s", err.what());
     }
@@ -1704,6 +1727,12 @@ Manager::scheduler()
     return pimpl_->scheduler_;
 }
 
+std::shared_ptr<asio::io_context>
+Manager::ioContext() const
+{
+    return pimpl_->ioContext_;
+}
+
 void
 Manager::addTask(std::function<bool()>&& task)
 {
diff --git a/src/manager.h b/src/manager.h
index 9d0a9b3d44d448ae5753c00bcce0abb39a6cc38a..34af7b30585ec4146cc47925563e87c6f6489121 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -44,6 +44,10 @@
 #include <functional>
 #include <algorithm>
 
+namespace asio {
+    class io_context;
+}
+
 namespace jami {
 
 namespace video {
@@ -858,6 +862,8 @@ class Manager {
 
         ScheduledExecutor& scheduler();
 
+        std::shared_ptr<asio::io_context> ioContext() const;
+
         void addTask(std::function<bool()>&& task);
         std::shared_ptr<Task> scheduleTask(std::function<void()>&& task, std::chrono::steady_clock::time_point when);