diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index ad3f1dc866bebff383de4dc9dc03dc524f59d990..99e08a4175ec071377a314ee5f5db34e6b216636 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -1519,6 +1519,8 @@ JamiAccount::doRegister()
     } else {
         doRegister_();
     }
+
+    cacheTurnServers(); // reset cache for TURN servers
 }
 
 std::vector<std::string>
@@ -1987,7 +1989,9 @@ JamiAccount::connectivityChanged()
     }
 
     dht_->connectivityChanged();
-    setPublishedAddress({}); // reset cache
+    // reset cache
+    setPublishedAddress({});
+    cacheTurnServers();
 }
 
 bool
@@ -2642,4 +2646,69 @@ JamiAccount::setActiveCodecs(const std::vector<unsigned>& list)
     }
 }
 
+void
+JamiAccount::cacheTurnServers()
+{
+    // The resolution of the TURN server can take quite some time (if timeout).
+    // So, run this in its own io thread to avoid to block the main thread.
+    dht::ThreadPool::io().run([this] {
+        // Avoid multiple refresh
+        if (isRefreshing_.exchange(true)) return;
+        if (!turnEnabled_) {
+            // In this case, we do not use any TURN server
+            std::lock_guard<std::mutex> lk(cachedTurnMutex_);
+            cacheTurnV4_.reset();
+            cacheTurnV6_.reset();
+            isRefreshing_ = false;
+            return;
+        }
+        JAMI_INFO("Refresh cache for TURN server resolution");
+        // Retrieve old cached value if available.
+        // This means that we directly get the correct value when launching the application on the same network
+        std::string server = turnServer_.empty() ? DEFAULT_TURN_SERVER : turnServer_;
+        fileutils::recursive_mkdir(cachePath_ + DIR_SEPARATOR_STR + "domains", 0700);
+        auto pathV4 = cachePath_ + DIR_SEPARATOR_STR + "domains" + DIR_SEPARATOR_STR + "v4." + server;
+        if (auto turnV4File = std::ifstream(pathV4)) {
+            std::string content((std::istreambuf_iterator<char>(turnV4File)), std::istreambuf_iterator<char>());
+            std::lock_guard<std::mutex> lk(cachedTurnMutex_);
+            cacheTurnV4_ = std::make_unique<IpAddr>(content, AF_INET);
+        }
+        auto pathV6 = cachePath_ + DIR_SEPARATOR_STR + "domains" + DIR_SEPARATOR_STR + "v6." + server;
+        if (auto turnV6File = std::ifstream(pathV6)) {
+            std::string content((std::istreambuf_iterator<char>(turnV6File)), std::istreambuf_iterator<char>());
+            std::lock_guard<std::mutex> lk(cachedTurnMutex_);
+            cacheTurnV6_ = std::make_unique<IpAddr>(content, AF_INET6);
+        }
+        // Resolve just in case. The user can have a different connectivity
+        auto turnV4 = IpAddr {server, AF_INET};
+        {
+            if (turnV4) {
+                // Cache value to avoid a delay when starting up Jami
+                std::ofstream turnV4File(pathV4);
+                turnV4File << turnV4.toString();
+            } else {
+                fileutils::remove(pathV4, true);
+            }
+            std::lock_guard<std::mutex> lk(cachedTurnMutex_);
+            // Update TURN
+            cacheTurnV4_ = std::make_unique<IpAddr>(std::move(turnV4));
+        }
+        auto turnV6 = IpAddr {server.empty() ? DEFAULT_TURN_SERVER : server, AF_INET6};
+        {
+            if (turnV6) {
+                // Cache value to avoid a delay when starting up Jami
+                std::ofstream turnV6File(pathV6);
+                turnV6File << turnV6.toString();
+            } else {
+                fileutils::remove(pathV6, true);
+            }
+            std::lock_guard<std::mutex> lk(cachedTurnMutex_);
+            // Update TURN
+            cacheTurnV6_ = std::make_unique<IpAddr>(std::move(turnV6));
+        }
+        isRefreshing_ = false;
+        JAMI_INFO("Cache refreshed for TURN resolution");
+    });
+}
+
 } // namespace jami
diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h
index 8b053481f8763df9319ee1f20502436e8b4ce10a..0596524400399dd4dc6c8448f3dc17d596c06f4e 100644
--- a/src/jamidht/jamiaccount.h
+++ b/src/jamidht/jamiaccount.h
@@ -646,6 +646,16 @@ private:
     bool accountPublish_ {false};
 
     std::shared_ptr<RepeatedTask> eventHandler {};
+
+    /**
+     * Avoid to refresh the cache multiple times
+     */
+    std::atomic_bool isRefreshing_ {false};
+    /**
+     * This will cache the turn server resolution each time we launch
+     * Jami, or for each connectivityChange()
+     */
+    void cacheTurnServers();
 };
 
 static inline std::ostream& operator<< (std::ostream& os, const JamiAccount& acc)
diff --git a/src/jamidht/p2p.cpp b/src/jamidht/p2p.cpp
index 89a6a65271616bb919f6ce02d4e5477946624d0a..2a0cf2d8a5a57ea1289cebb6f4af29ba31a9435c 100644
--- a/src/jamidht/p2p.cpp
+++ b/src/jamidht/p2p.cpp
@@ -548,8 +548,14 @@ DhtPeerConnector::Impl::turnConnect()
     auto username = details[Conf::CONFIG_TURN_SERVER_UNAME];
     auto password = details[Conf::CONFIG_TURN_SERVER_PWD];
 
+    auto turnCache = account.turnCache();
+
     auto turn_param_v4 = TurnTransportParams {};
-    turn_param_v4.server = IpAddr {server.empty() ? "turn.jami.net" : server};
+    if (turnCache[0]) {
+        turn_param_v4.server = *turnCache[0];
+    } else {
+        turn_param_v4.server = IpAddr {server.empty() ? "turn.jami.net" : server};
+    }
     turn_param_v4.realm = realm.empty() ? "ring" : realm;
     turn_param_v4.username = username.empty() ? "ring" : username;
     turn_param_v4.password = password.empty() ? "ring" : password;
@@ -576,6 +582,11 @@ DhtPeerConnector::Impl::turnConnect()
 
         if (!turnAuthv6_ || !turnAuthv6_->isReady()) {
             auto turn_param_v6 = turn_param_v4;
+            if (turnCache[1]) {
+                turn_param_v6.server = *turnCache[1];
+            } else {
+                turn_param_v6.server = IpAddr {server.empty() ? "turn.jami.net" : server};
+            }
             turn_param_v6.authorized_family = PJ_AF_INET6;
             turnAuthv6_ = std::make_unique<TurnTransport>(turn_param_v6);
         }
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index 0997c765ec2f9c254abe06c847f753b69907c7d5..9427b136345da2b092939205282efd3e6fe82680 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -370,15 +370,35 @@ SIPAccountBase::getIceOptions() const noexcept
 {
     auto opts = Account::getIceOptions(); // Local copy of global account ICE settings
 
-    // Note: we don't check of servers pre-existance, let underlaying stack do the job
     if (stunEnabled_)
         opts.stunServers.emplace_back(StunServerInfo().setUri(stunServer_));
-    if (turnEnabled_)
-        opts.turnServers.emplace_back(TurnServerInfo()
-                                      .setUri(turnServer_)
-                                      .setUsername(turnServerUserName_)
-                                      .setPassword(turnServerPwd_)
-                                      .setRealm(turnServerRealm_));
+    if (turnEnabled_) {
+        auto cached = false;
+        std::lock_guard<std::mutex> lk(cachedTurnMutex_);
+        cached = cacheTurnV4_ || cacheTurnV6_;
+        if (cacheTurnV4_ && *cacheTurnV4_) {
+            opts.turnServers.emplace_back(TurnServerInfo()
+                                .setUri(*cacheTurnV4_)
+                                .setUsername(turnServerUserName_)
+                                .setPassword(turnServerPwd_)
+                                .setRealm(turnServerRealm_));
+        }
+        if (cacheTurnV6_ && *cacheTurnV6_) {
+            opts.turnServers.emplace_back(TurnServerInfo()
+                                .setUri(*cacheTurnV4_)
+                                .setUsername(turnServerUserName_)
+                                .setPassword(turnServerPwd_)
+                                .setRealm(turnServerRealm_));
+        }
+        // Nothing cached, so do the resolution
+        if (!cached) {
+            opts.turnServers.emplace_back(TurnServerInfo()
+                                        .setUri(turnServer_)
+                                        .setUsername(turnServerUserName_)
+                                        .setPassword(turnServerPwd_)
+                                        .setRealm(turnServerRealm_));
+        }
+    }
     return opts;
 }
 
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index 6235313e9db6449662f3f41c710dea7bed4550aa..ea9ab75f72bd292f8708ed6ac43cf1cebfd1a8a9 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -298,6 +298,20 @@ public:
 public: // overloaded methods
     virtual void flush() override;
 
+    /**
+     * Return current turn resolved addresses
+     * @return {unique_ptr(v4 resolved), unique_ptr(v6 resolved)}
+     */
+    std::array<std::unique_ptr<IpAddr>, 2> turnCache() {
+        std::lock_guard<std::mutex> lk {cachedTurnMutex_};
+        std::array<std::unique_ptr<IpAddr>, 2> result = {};
+        if (cacheTurnV4_ && *cacheTurnV4_)
+            result[0] = std::make_unique<IpAddr>(*cacheTurnV4_);
+        if (cacheTurnV6_ && *cacheTurnV6_)
+            result[1] = std::make_unique<IpAddr>(*cacheTurnV6_);
+        return result;
+    }
+
 protected:
     virtual void serialize(YAML::Emitter &out) const override;
     virtual void serializeTls(YAML::Emitter &out) const;
@@ -445,6 +459,10 @@ protected:
     static constexpr size_t MAX_WAITING_MESSAGES_SIZE = 1000;
     std::deque<DRing::Message> lastMessages_;
 
+    mutable std::mutex cachedTurnMutex_ {};
+    std::unique_ptr<IpAddr> cacheTurnV4_ {};
+    std::unique_ptr<IpAddr> cacheTurnV6_ {};
+
 private:
     NON_COPYABLE(SIPAccountBase);