diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index c1362dbf5a..5415fabdf4 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -67,6 +67,10 @@
 # (integer value)
 #hash_distribution_replicas=1
 
+# Interval (in seconds) between hash ring resets. (integer
+# value)
+#hash_ring_reset_interval=180
+
 
 #
 # Options defined in ironic.common.images
diff --git a/ironic/common/hash_ring.py b/ironic/common/hash_ring.py
index b345251caa..baeb957432 100644
--- a/ironic/common/hash_ring.py
+++ b/ironic/common/hash_ring.py
@@ -16,6 +16,7 @@
 import bisect
 import hashlib
 import threading
+import time
 
 from oslo_config import cfg
 import six
@@ -47,6 +48,9 @@ hash_opts = [
                       'conductor services to prepare deployment environments '
                       'and potentially allow the Ironic cluster to recover '
                       'more quickly if a conductor instance is terminated.')),
+    cfg.IntOpt('hash_ring_reset_interval',
+               default=180,
+               help=_('Interval (in seconds) between hash ring resets.')),
 ]
 
 CONF = cfg.CONF
@@ -166,17 +170,21 @@ class HashRingManager(object):
 
     def __init__(self):
         self.dbapi = dbapi.get_instance()
+        self.updated_at = time.time()
 
     @property
     def ring(self):
+        interval = CONF.hash_ring_reset_interval
+        limit = time.time() - interval
         # Hot path, no lock
-        if self._hash_rings is not None:
+        if self._hash_rings is not None and self.updated_at >= limit:
             return self._hash_rings
 
         with self._lock:
-            if self._hash_rings is None:
+            if self._hash_rings is None or self.updated_at < limit:
                 rings = self._load_hash_rings()
                 self.__class__._hash_rings = rings
+                self.updated_at = time.time()
             return self._hash_rings
 
     def _load_hash_rings(self):
diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py
index 0f01ec5baf..9312ea8e2d 100644
--- a/ironic/conductor/manager.py
+++ b/ironic/conductor/manager.py
@@ -1406,7 +1406,6 @@ class ConductorManager(periodic_task.PeriodicTasks):
         The ensuing actions could include preparing a PXE environment,
         updating the DHCP server, and so on.
         """
-        self.ring_manager.reset()
         filters = {'reserved': False,
                    'maintenance': False,
                    'provision_state': states.ACTIVE}
diff --git a/ironic/tests/unit/common/test_hash_ring.py b/ironic/tests/unit/common/test_hash_ring.py
index 832c975b28..b5a36c2d9a 100644
--- a/ironic/tests/unit/common/test_hash_ring.py
+++ b/ironic/tests/unit/common/test_hash_ring.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import hashlib
+import time
 
 import mock
 from oslo_config import cfg
@@ -249,3 +250,14 @@ class HashRingManagerTestCase(db_base.DbTestCase):
         self.assertRaises(exception.DriverNotFound,
                           self.ring_manager.__getitem__,
                           'driver1')
+
+    def test_hash_ring_manager_refresh(self):
+        CONF.set_override('hash_ring_reset_interval', 30)
+        # Initialize the ring manager to make _hash_rings not None, then
+        # hash ring will refresh only when time interval exceeded.
+        self.assertRaises(exception.DriverNotFound,
+                          self.ring_manager.__getitem__,
+                          'driver1')
+        self.register_conductors()
+        self.ring_manager.updated_at = time.time() - 30
+        self.ring_manager.__getitem__('driver1')
diff --git a/ironic/tests/unit/conductor/test_manager.py b/ironic/tests/unit/conductor/test_manager.py
index f86026bb30..942249fd45 100644
--- a/ironic/tests/unit/conductor/test_manager.py
+++ b/ironic/tests/unit/conductor/test_manager.py
@@ -3830,7 +3830,6 @@ class ManagerSyncLocalStateTestCase(_CommonMixIn, tests_db_base.DbTestCase):
         self._assert_get_nodeinfo_args(get_nodeinfo_mock)
         mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver)
         self.assertFalse(acquire_mock.called)
-        self.service.ring_manager.reset.assert_called_once_with()
 
     def test_already_mapped(self, get_nodeinfo_mock, mapped_mock,
                             acquire_mock):
@@ -3846,7 +3845,6 @@ class ManagerSyncLocalStateTestCase(_CommonMixIn, tests_db_base.DbTestCase):
         self._assert_get_nodeinfo_args(get_nodeinfo_mock)
         mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver)
         self.assertFalse(acquire_mock.called)
-        self.service.ring_manager.reset.assert_called_once_with()
 
     def test_good(self, get_nodeinfo_mock, mapped_mock, acquire_mock):
         get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()