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 f32de8dff441b6832a906f32681182c7a9233f45..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())
@@ -438,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.
@@ -446,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(),
+        ),
     )
 
 
@@ -486,7 +501,7 @@ class MemoryMaintenanceObserver(object):
 
 
 def maintain_leases_from_root(
-        root_node,
+        get_root_nodes,
         storage_broker,
         secret_holder,
         min_lease_remaining,
@@ -498,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
@@ -529,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 5b90fe3654839390e7a551bfc80b1d0a2bfd5160..4e0143ce899ee19136c8ed309e902d013d989ea6 100644
--- a/src/_zkapauthorizer/tests/test_lease_maintenance.py
+++ b/src/_zkapauthorizer/tests/test_lease_maintenance.py
@@ -411,7 +411,7 @@ class VisitStorageIndexesFromRootTests(TestCase):
 
         operation = visit_storage_indexes_from_root(
             perform_visit,
-            root_node,
+            lambda: [root_node],
         )
 
         self.assertThat(
@@ -516,7 +516,7 @@ class MaintainLeasesFromRootTests(TestCase):
             )
 
         operation = maintain_leases_from_root(
-            root_node,
+            lambda: [root_node],
             storage_broker,
             secret_holder,
             min_lease_remaining,
@@ -569,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,
diff --git a/src/_zkapauthorizer/tests/test_plugin.py b/src/_zkapauthorizer/tests/test_plugin.py
index c6b4e89d0cff64d7870ae4588cdd2f1bc725bdd1..55c30a5d2205b5427300359c427f33c95482dca1 100644
--- a/src/_zkapauthorizer/tests/test_plugin.py
+++ b/src/_zkapauthorizer/tests/test_plugin.py
@@ -441,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())
@@ -455,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)
@@ -492,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)