diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py
index 1ec0f75305d2307cafeedd7187f08ef3e1779fa0..4ba84ddd840e6a56e7cf54fd0ed6ae8bc3449f40 100644
--- a/src/_zkapauthorizer/_plugin.py
+++ b/src/_zkapauthorizer/_plugin.py
@@ -25,6 +25,10 @@ from datetime import (
     datetime,
     timedelta,
 )
+from functools import (
+    partial,
+)
+
 import attr
 
 from zope.interface import (
@@ -45,6 +49,9 @@ from allmydata.interfaces import (
     IFoolscapStoragePlugin,
     IAnnounceableStorageServer,
 )
+from allmydata.node import (
+    MissingConfigEntry,
+)
 from allmydata.client import (
     _Client,
 )
@@ -238,9 +245,7 @@ def _create_maintenance_service(reactor, node_config, client_node):
     # Create the operation which performs the lease maintenance job when
     # called.
     maintain_leases = maintain_leases_from_root(
-        client_node.create_node_from_uri(
-            node_config.get_private_config(b"rootcap"),
-        ),
+        partial(get_root_nodes, client_node, node_config),
         client_node.get_storage_broker(),
         client_node._secret_holder,
         # Make this configuration
@@ -256,3 +261,12 @@ def _create_maintenance_service(reactor, node_config, client_node):
         last_run_path,
         random,
     )
+
+
+def get_root_nodes(client_node, node_config):
+    try:
+        rootcap = node_config.get_private_config(b"rootcap")
+    except MissingConfigEntry:
+        return []
+    else:
+        return [client_node.create_node_from_uri(rootcap)]
diff --git a/src/_zkapauthorizer/lease_maintenance.py b/src/_zkapauthorizer/lease_maintenance.py
index e264f32ca8a0dd60b362da5ca01150563d092173..8a3a0956660984a5eb1b5d05cf8fb04759f6870d 100644
--- a/src/_zkapauthorizer/lease_maintenance.py
+++ b/src/_zkapauthorizer/lease_maintenance.py
@@ -50,6 +50,7 @@ from twisted.python.log import (
 
 from allmydata.interfaces import (
     IDirectoryNode,
+    IFilesystemNode,
 )
 from allmydata.util.hashutil import (
     file_renewal_secret_hash,
@@ -68,7 +69,7 @@ SERVICE_NAME = u"lease maintenance service"
 
 
 @inlineCallbacks
-def visit_storage_indexes(root_node, visit):
+def visit_storage_indexes(root_nodes, visit):
     """
     Call a visitor with the storage index of ``root_node`` and that of all
     nodes reachable from it.
@@ -81,7 +82,17 @@ def visit_storage_indexes(root_node, visit):
     :return Deferred: A Deferred which fires after all nodes have been
         visited.
     """
-    stack = [root_node]
+    if not isinstance(root_nodes, list):
+        raise TypeError("root_nodes must be a list, not {!r}".format(
+            root_nodes,
+        ))
+    for node in root_nodes:
+        if not IFilesystemNode.providedBy(node):
+            raise TypeError("Root nodes must provide IFilesystemNode, {!r} does not".format(
+                node,
+            ))
+
+    stack = root_nodes[:]
     while stack:
         elem = stack.pop()
         visit(elem.get_storage_index())
@@ -154,7 +165,11 @@ def renew_leases(
     storage_indexes = yield iter_storage_indexes(visit_assets)
 
     renewal_secret = secret_holder.get_renewal_secret()
-    servers = storage_broker.get_connected_servers()
+    servers = list(
+        server.get_storage_server()
+        for server
+        in storage_broker.get_connected_servers()
+    )
 
     for server in servers:
         # Consider parallelizing this.
@@ -434,7 +449,7 @@ def read_time_from_path(path):
         return parse_datetime(when)
 
 
-def visit_storage_indexes_from_root(visitor, root_node):
+def visit_storage_indexes_from_root(visitor, get_root_nodes):
     """
     An operation for ``lease_maintenance_service`` which applies the given
     visitor to ``root_node`` and all its children.
@@ -442,14 +457,18 @@ def visit_storage_indexes_from_root(visitor, root_node):
     :param visitor: A one-argument callable which takes the traversal function
         and which should call it as desired.
 
-    :param IFilesystemNode root_node: The filesystem node at which traversal
-        will begin.
+    :param get_root_nodes: A no-argument callable which returns a list of
+        filesystem nodes (``IFilesystemNode``) at which traversal will begin.
 
     :return: A no-argument callable to perform the visits.
     """
-    return partial(
-        visitor,
-        partial(visit_storage_indexes, root_node),
+    return lambda: visitor(
+        partial(
+            visit_storage_indexes,
+            # Make sure we call get_root_nodes each time to give us a chance
+            # to notice when it changes.
+            get_root_nodes(),
+        ),
     )
 
 
@@ -482,7 +501,7 @@ class MemoryMaintenanceObserver(object):
 
 
 def maintain_leases_from_root(
-        root_node,
+        get_root_nodes,
         storage_broker,
         secret_holder,
         min_lease_remaining,
@@ -494,8 +513,9 @@ def maintain_leases_from_root(
     and all its children and renews their leases if they have
     ``min_lease_remaining`` or less on them.
 
-    :param IFilesystemNode root_node: A Tahoe-LAFS filesystem node to use as
-        the root of a node hierarchy to be maintained.
+    :param get_root_nodes: A no-argument callable which returns the list of
+        Tahoe-LAFS filesystem nodes (``IFilesystemNode``) to use as the roots
+        of the node hierarchies to be maintained.
 
     :param StorageFarmBroker storage_broker: The storage broker which can put
         us in touch with storage servers where shares of the nodes to maintain
@@ -525,7 +545,7 @@ def maintain_leases_from_root(
 
     return visit_storage_indexes_from_root(
         visitor,
-        root_node,
+        get_root_nodes,
     )
 
 
diff --git a/src/_zkapauthorizer/tests/test_lease_maintenance.py b/src/_zkapauthorizer/tests/test_lease_maintenance.py
index 7d4df1de10ab11a5bdb1df2522c97d2bfce64d6c..4e0143ce899ee19136c8ed309e902d013d989ea6 100644
--- a/src/_zkapauthorizer/tests/test_lease_maintenance.py
+++ b/src/_zkapauthorizer/tests/test_lease_maintenance.py
@@ -28,6 +28,10 @@ from datetime import (
 
 import attr
 
+from zope.interface import (
+    implementer,
+)
+
 from testtools import (
     TestCase,
 )
@@ -82,6 +86,10 @@ from allmydata.util.hashutil import (
 from allmydata.client import (
     SecretHolder,
 )
+from allmydata.interfaces import (
+    IStorageBroker,
+    IServer,
+)
 
 from ..foolscap import (
     ShareStat,
@@ -179,11 +187,29 @@ def storage_servers(clocks):
         clocks,
         dictionaries(storage_indexes(), share_stats()),
         lease_seeds(),
+    ).map(
+        DummyServer,
     )
 
 
+@implementer(IServer)
+@attr.s
+class DummyServer(object):
+    """
+    A partial implementation of a Tahoe-LAFS "native" storage server.
+    """
+    _storage_server = attr.ib()
+
+    def get_storage_server(self):
+        return self._storage_server
+
+
+@implementer(IStorageBroker)
 @attr.s
 class DummyStorageBroker(object):
+    """
+    A partial implementation of a Tahoe-LAFS storage broker.
+    """
     clock = attr.ib()
     _storage_servers = attr.ib()
 
@@ -385,7 +411,7 @@ class VisitStorageIndexesFromRootTests(TestCase):
 
         operation = visit_storage_indexes_from_root(
             perform_visit,
-            root_node,
+            lambda: [root_node],
         )
 
         self.assertThat(
@@ -455,7 +481,11 @@ class RenewLeasesTests(TestCase):
         )
 
         self.assertThat(
-            storage_broker.get_connected_servers(),
+            list(
+                server.get_storage_server()
+                for server
+                in storage_broker.get_connected_servers()
+            ),
             AllMatch(leases_current(
                 relevant_storage_indexes,
                 get_now(),
@@ -486,7 +516,7 @@ class MaintainLeasesFromRootTests(TestCase):
             )
 
         operation = maintain_leases_from_root(
-            root_node,
+            lambda: [root_node],
             storage_broker,
             secret_holder,
             min_lease_remaining,
@@ -506,7 +536,11 @@ class MaintainLeasesFromRootTests(TestCase):
         )
 
         self.assertThat(
-            storage_broker.get_connected_servers(),
+            list(
+                server.get_storage_server()
+                for server
+                in storage_broker.get_connected_servers()
+            ),
             AllMatch(leases_current(
                 relevant_storage_indexes,
                 get_now(),
@@ -535,7 +569,7 @@ class MaintainLeasesFromRootTests(TestCase):
         observers = [observer]
         progress = observers.pop
         operation = maintain_leases_from_root(
-            root_node,
+            lambda: [root_node],
             storage_broker,
             secret_holder,
             min_lease_remaining,
@@ -550,9 +584,9 @@ class MaintainLeasesFromRootTests(TestCase):
 
         expected = []
         for node in root_node.flatten():
-            for storage_server in storage_broker.get_connected_servers():
+            for server in storage_broker.get_connected_servers():
                 try:
-                    stat = storage_server.buckets[node.get_storage_index()]
+                    stat = server.get_storage_server().buckets[node.get_storage_index()]
                 except KeyError:
                     continue
                 else:
diff --git a/src/_zkapauthorizer/tests/test_plugin.py b/src/_zkapauthorizer/tests/test_plugin.py
index 325a87b941320179ec63501616625973e868f82c..55c30a5d2205b5427300359c427f33c95482dca1 100644
--- a/src/_zkapauthorizer/tests/test_plugin.py
+++ b/src/_zkapauthorizer/tests/test_plugin.py
@@ -52,6 +52,7 @@ from testtools.content import (
 )
 from hypothesis import (
     given,
+    settings,
 )
 from hypothesis.strategies import (
     just,
@@ -440,7 +441,7 @@ class LeaseMaintenanceServiceTests(TestCase):
     """
     Tests for the plugin's initialization of the lease maintenance service.
     """
-    def _created_test(self, get_config, servers_yaml):
+    def _created_test(self, get_config, servers_yaml, rootcap):
         original_tempdir = tempfile.tempdir
 
         tempdir = self.useFixture(TempDir())
@@ -454,10 +455,11 @@ class LeaseMaintenanceServiceTests(TestCase):
             b"servers.yaml",
             servers_yaml,
         )
-        config.write_private_config(
-            b"rootcap",
-            b"dddddddd",
-        )
+        if rootcap:
+            config.write_private_config(
+                b"rootcap",
+                b"dddddddd",
+            )
 
         try:
             d = create_client_from_config(config)
@@ -478,6 +480,9 @@ class LeaseMaintenanceServiceTests(TestCase):
             # of those at the right time. :/
            tempfile.tempdir = original_tempdir
 
+    @settings(
+        deadline=None,
+    )
     @given(
         tahoe_configs_with_dummy_redeemer,
         sampled_from([SERVERS_YAML, TWO_SERVERS_YAML]),
@@ -488,4 +493,19 @@ class LeaseMaintenanceServiceTests(TestCase):
         maintenance service after it has at least one storage server to
         connect to.
         """
-        return self._created_test(get_config, servers_yaml)
+        return self._created_test(get_config, servers_yaml, rootcap=True)
+
+
+    @settings(
+        deadline=None,
+    )
+    @given(
+        tahoe_configs_with_dummy_redeemer,
+        sampled_from([SERVERS_YAML, TWO_SERVERS_YAML]),
+    )
+    def test_created_without_rootcap(self, get_config, servers_yaml):
+        """
+        The lease maintenance service can be created even if no rootcap has yet
+        been written to the client's configuration directory.
+        """
+        return self._created_test(get_config, servers_yaml, rootcap=False)