diff --git a/src/ZEO/mkzeoinst.py b/src/ZEO/mkzeoinst.py
index d74e8d5e3833c1d2ecc30d760a3552b511a70992..fe6c8210838953daf030cd28e9961d81332fb6fd 100755
--- a/src/ZEO/mkzeoinst.py
+++ b/src/ZEO/mkzeoinst.py
@@ -20,11 +20,10 @@ Given an "instance home directory" <home> and some configuration
 options (all of which have default values), create the following:
 
 <home>/etc/zeo.conf     -- ZEO config file
-<home>/etc/zeoctl.conf  -- zdctl+zdrun config file
 <home>/var/             -- Directory for data files: Data.fs etc.
 <home>/log/             -- Directory for log files: zeo.log and zeoctl.log
 <home>/bin/runzeo       -- the zeo server runner
-<home>/bin/zeoctl       -- start/stop script (a shim for zdctl.py)
+<home>/bin/zeoctl       -- start/stop script (a shim for zeoctl.py)
 
 The script will not overwrite existing files; instead, it will issue a
 warning if an existing file is found that differs from the file that
@@ -39,9 +38,10 @@ import sys
 import stat
 import getopt
 
-zeo_conf_template = """# ZEO configuration file
+zeo_conf_template = """\
+# ZEO configuration file
 
-%%define INSTANCE_HOME %(instance_home)s
+%%define INSTANCE %(instance_home)s
 
 <zeo>
   address %(port)d
@@ -52,52 +52,42 @@ zeo_conf_template = """# ZEO configuration file
 </zeo>
 
 <filestorage 1>
-  path $INSTANCE_HOME/var/Data.fs
+  path $INSTANCE/var/Data.fs
 </filestorage>
 
 <eventlog>
   level info
   <logfile>
-    path $INSTANCE_HOME/log/zeo.log
+    path $INSTANCE/log/zeo.log
   </logfile>
 </eventlog>
-"""
-
-runner_conf_template = """# %(package)sctl configuration file
-
-%%define INSTANCE_HOME %(instance_home)s
 
 <runner>
-  program $INSTANCE_HOME/bin/runzeo
-  socket-name $INSTANCE_HOME/etc/%(package)s.zdsock
+  program $INSTANCE/bin/runzeo
+  socket-name $INSTANCE/etc/%(package)s.zdsock
   daemon true
   forever false
   backoff-limit 10
   exit-codes 0, 2
-  directory $INSTANCE_HOME
+  directory $INSTANCE
   default-to-interactive true
   # user zope
   python %(python)s
   zdrun %(zope_home)s/zdaemon/zdrun.py
+
   # This logfile should match the one in the %(package)s.conf file.
   # It is used by zdctl's logtail command, zdrun/zdctl doesn't write it.
-  logfile $INSTANCE_HOME/log/%(package)s.log
+  logfile $INSTANCE/log/%(package)s.log
 </runner>
-
-<eventlog>
-  level info
-  <logfile>
-    path $INSTANCE_HOME/log/%(package)sctl.log
-  </logfile>
-</eventlog>
 """
 
-zdctl_template = """#!/bin/sh
-# %(PACKAGE)s instance start script
+zeoctl_template = """\
+#!/bin/sh
+# %(PACKAGE)s instance control script
 
 # The following two lines are for chkconfig.  On Red Hat Linux (and
 # some other systems), you can copy or symlink this script into
-# /etc/rc.d/init.d/ and then run chkconfig(8), to automatically start
+# /etc/rc.d/init.d/ and then use chkconfig(8) to automatically start
 # %(PACKAGE)s at boot time.
 
 # chkconfig: 345 90 10
@@ -105,33 +95,32 @@ zdctl_template = """#!/bin/sh
 
 PYTHON="%(python)s"
 ZOPE_HOME="%(zope_home)s"
-INSTANCE_HOME="%(instance_home)s"
 
-CONFIG_FILE="$INSTANCE_HOME/etc/%(package)sctl.conf"
+CONFIG_FILE="%(instance_home)s/etc/%(package)s.conf"
 
 PYTHONPATH="$ZOPE_HOME"
 export PYTHONPATH
 
-ZDCTL="$ZOPE_HOME/zdaemon/zdctl.py"
+ZEOCTL="$ZOPE_HOME/ZEO/zeoctl.py"
 
-exec "$PYTHON" "$ZDCTL" -C "$CONFIG_FILE" ${1+"$@"}
+exec "$PYTHON" "$ZEOCTL" -C "$CONFIG_FILE" ${1+"$@"}
 """
 
-runzeo_template = """#!/bin/sh
+runzeo_template = """\
+#!/bin/sh
 # %(PACKAGE)s instance start script
 
 PYTHON="%(python)s"
 ZOPE_HOME="%(zope_home)s"
-INSTANCE_HOME="%(instance_home)s"
 
-CONFIG_FILE="$INSTANCE_HOME/etc/%(package)s.conf"
+CONFIG_FILE="%(instance_home)s/etc/%(package)s.conf"
 
 PYTHONPATH="$ZOPE_HOME"
 export PYTHONPATH
 
-ZEO_RUN="$ZOPE_HOME/ZEO/runzeo.py"
+RUNZEO="$ZOPE_HOME/ZEO/runzeo.py"
 
-exec "$PYTHON" "$ZEO_RUN" -C "$CONFIG_FILE" ${1+"$@"}
+exec "$PYTHON" "$RUNZEO" -C "$CONFIG_FILE" ${1+"$@"}
 """
 
 def main():
@@ -193,8 +182,7 @@ class ZEOInstanceBuilder:
         makedir(home, "log")
         makedir(home, "bin")
         makefile(zeo_conf_template, home, "etc", "zeo.conf", **params)
-        makefile(runner_conf_template, home, "etc", "zeoctl.conf", **params)
-        makexfile(zdctl_template, home, "bin", "zeoctl", **params)
+        makexfile(zeoctl_template, home, "bin", "zeoctl", **params)
         makexfile(runzeo_template, home, "bin", "runzeo", **params)
 
 
diff --git a/src/ZEO/runzeo.py b/src/ZEO/runzeo.py
index d3b2fda3f6846e3cca3ac20e466a8ee704e89d8f..6a439a640c11d5dcc300c452e2e78732ac29b482 100644
--- a/src/ZEO/runzeo.py
+++ b/src/ZEO/runzeo.py
@@ -99,9 +99,9 @@ class ZEOOptionsMixin:
 class ZEOOptions(ZDOptions, ZEOOptionsMixin):
 
     logsectionname = "eventlog"
+    schemadir = os.path.dirname(ZEO.__file__)
 
     def __init__(self):
-        self.schemadir = os.path.dirname(ZEO.__file__)
         ZDOptions.__init__(self)
         self.add_zeo_options()
         self.add("storages", "storages",
diff --git a/src/ZEO/schema.xml b/src/ZEO/schema.xml
index 59286f36293d92102f0251e255dacf2020519fa8..394841129eb1de24ef701b2aff6000238c8bca76 100644
--- a/src/ZEO/schema.xml
+++ b/src/ZEO/schema.xml
@@ -1,5 +1,8 @@
 <schema>
 
+  <!-- note that zeoctl.xml is a closely related schema which should
+       match this schema, but should require the "runner" section -->
+
   <description>
     This schema describes the configuration of the ZEO storage server
     process.
@@ -13,9 +16,14 @@
 
   <import package="zLOG"/>
 
+  <!-- runner control -->
+  <import package="zdaemon"/>
+
 
   <section type="zeo" name="*" required="yes" attribute="zeo" />
 
+  <section type="runner" name="*" required="no" attribute="runner" />
+
   <multisection name="+" type="ZODB.storage"
                 attribute="storages"
                 required="yes">
diff --git a/src/ZEO/zeoctl.py b/src/ZEO/zeoctl.py
new file mode 100644
index 0000000000000000000000000000000000000000..82e67bc4362b490f2b8e3501a6ff92f5346fa4be
--- /dev/null
+++ b/src/ZEO/zeoctl.py
@@ -0,0 +1,19 @@
+"""Wrapper script for zdctl.py that causes it to use the ZEO schema."""
+
+import os
+
+import ZEO
+import zLOG
+import zdaemon.zdctl
+
+
+# Main program
+def main(args=None):
+    options = zdaemon.zdctl.ZDCtlOptions()
+    options.schemadir = os.path.dirname(ZEO.__file__)
+    options.schemafile = "zeoctl.xml"
+    zdaemon.zdctl.main(args, options)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/src/ZEO/zeoctl.xml b/src/ZEO/zeoctl.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4d65078796f285f8258bee1b9054602a1328b4e6
--- /dev/null
+++ b/src/ZEO/zeoctl.xml
@@ -0,0 +1,31 @@
+<schema>
+
+  <description>
+    This schema describes the configuration of the ZEO storage server
+    controller.  It differs from the schema for the storage server
+    only in that the "runner" section is required.
+  </description>
+
+  <!-- Use the storage types defined by ZODB. -->
+  <import package="ZODB"/>
+
+  <!-- Use the ZEO server information structure. -->
+  <import package="ZEO"/>
+
+  <import package="zLOG"/>
+
+  <!-- runner control -->
+  <import package="zdaemon"/>
+
+
+  <section type="zeo" name="*" required="yes" attribute="zeo" />
+
+  <section type="runner" name="*" required="yes" attribute="runner" />
+
+  <multisection name="+" type="ZODB.storage"
+                attribute="storages"
+                required="yes" />
+
+  <section name="*" type="eventlog" attribute="eventlog" required="no" />
+
+</schema>