diff --git a/morph/grid/local/grid.nix b/morph/grid/local/grid.nix
index 4a1524c6b6b7f5e085766aec6a79af5b569e72ba..c686cdd2582a659c0feef7440f815a752174cdaf 100644
--- a/morph/grid/local/grid.nix
+++ b/morph/grid/local/grid.nix
@@ -59,6 +59,7 @@ let
     grid = {
       publicKeyPath = toString ./. + "/${grid-config.publicKeyPath}";
       privateKeyPath = toString ./. + "/${grid-config.privateKeyPath}";
+      inherit (grid-config) monitoringvpnEndpoint;
     };
     # Configure deployment management authorization for all systems in the grid.
     services.private-storage.deployment = {
@@ -70,12 +71,11 @@ let
   payments = {
     imports = [
       gridlib.issuer
-      (gridlib.customize-issuer (grid-config // {
-          monitoringvpnIPv4 = "172.23.23.11";
-      }))
+      (gridlib.customize-issuer grid-config)
       grid-module
     ];
     config = {
+      grid.monitoringvpnIPv4 = "172.23.23.11";
       grid.publicIPv4 = "192.168.67.21";
     };
   };
@@ -84,12 +84,12 @@ let
     imports = [
       gridlib.storage
       (gridlib.customize-storage (grid-config // {
-        monitoringvpnIPv4 = "172.23.23.12";
         stateVersion = "19.09";
       }))
       grid-module
     ];
     config = {
+      grid.monitoringvpnIPv4 = "172.23.23.12";
       grid.publicIPv4 = "192.168.67.22";
     };
   };
@@ -98,12 +98,12 @@ let
     imports = [
       gridlib.storage
       (gridlib.customize-storage (grid-config // {
-        monitoringvpnIPv4 = "172.23.23.13";
         stateVersion = "19.09";
       }))
       grid-module
     ];
     config = {
+      grid.monitoringvpnIPv4 = "172.23.23.13";
       grid.publicIPv4 = "192.168.67.23";
     };
   };
@@ -119,12 +119,12 @@ let
         inherit (grid-config) letsEncryptAdminEmail monitoringDomains;
         googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
         enableSlackAlert = false;
-        monitoringvpnIPv4 = "172.23.23.1";
         stateVersion = "19.09";
       })
       grid-module
     ];
     config = {
+      grid.monitoringvpnIPv4 = "172.23.23.1";
       grid.publicIPv4 = "192.168.67.24";
     };
   };
diff --git a/morph/grid/production/grid.nix b/morph/grid/production/grid.nix
index 950282f5573560f76355bcdcf4d6da51dacedd7d..131978dc437d52dd3dedb93b8c7a8888e7fe72e6 100644
--- a/morph/grid/production/grid.nix
+++ b/morph/grid/production/grid.nix
@@ -21,6 +21,7 @@ let
     grid = {
       publicKeyPath = toString ./. + "/${grid-config.publicKeyPath}";
       privateKeyPath = toString ./. + "/${grid-config.privateKeyPath}";
+      inherit (grid-config) monitoringvpnEndpoint;
     };
     # Configure deployment management authorization for all systems in the grid.
     services.private-storage.deployment = {
@@ -33,11 +34,12 @@ let
     imports = [
       gridlib.issuer
       gridlib.hardware-aws
-      (gridlib.customize-issuer (grid-config // {
-        monitoringvpnIPv4 = "172.23.23.11";
-      }))
+      (gridlib.customize-issuer grid-config)
       grid-module
     ];
+    config = {
+      grid.monitoringvpnIPv4 = "172.23.23.11";
+    };
   };
 
   monitoring = {
@@ -52,11 +54,13 @@ let
         inherit (grid-config) letsEncryptAdminEmail monitoringDomains;
         googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
         enableSlackAlert = true;
-        monitoringvpnIPv4 = "172.23.23.1";
         stateVersion = "19.09";
       })
       grid-module
     ];
+    config = {
+      grid.monitoringvpnIPv4 = "172.23.23.1";
+    };
   };
 
   defineStorageNode = name: { vpnIP, stateVersion }:
@@ -82,7 +86,6 @@ let
 
       # Then customize the storage system a little bit based on this node's particulars.
       (gridlib.customize-storage (grid-config // nodecfg // {
-        monitoringvpnIPv4 = vpnIP;
         inherit stateVersion;
       }))
 
@@ -90,15 +93,19 @@ let
       grid-module
     ];
 
-    # And supply configuration for those hardware / network / bootloader
-    # options.  See the 100tb module for handling of this value.  The module
-    # name is quoted because `1` makes `100tb` look an awful lot like a
-    # number.
-   "100tb".config = nodecfg;
+    config = {
+      grid.monitoringvpnIPv4 = vpnIP;
 
-    # Enable statistics gathering for MegaRAID cards.
-    # TODO would be nice to enable only on machines that have such a device.
-    services.private-storage.monitoring.megacli2prom.enable = true;
+      # And supply configuration for those hardware / network / bootloader
+      # options.  See the 100tb module for handling of this value.  The module
+      # name is quoted because `1` makes `100tb` look an awful lot like a
+      # number.
+      "100tb".config = nodecfg;
+
+      # Enable statistics gathering for MegaRAID cards.
+      # TODO would be nice to enable only on machines that have such a device.
+      services.private-storage.monitoring.megacli2prom.enable = true;
+    };
   };
 
   # Define all of the storage nodes for this grid.
diff --git a/morph/grid/testing/grid.nix b/morph/grid/testing/grid.nix
index 334518774851c22738c93b323223f255d871a394..a2d844a26f9bd0acc3b279d8d630b0108bc1f39b 100644
--- a/morph/grid/testing/grid.nix
+++ b/morph/grid/testing/grid.nix
@@ -21,6 +21,7 @@ let
     grid = {
       publicKeyPath = toString ./. + "/${grid-config.publicKeyPath}";
       privateKeyPath = toString ./. + "/${grid-config.privateKeyPath}";
+      inherit (grid-config) monitoringvpnEndpoint;
     };
     # Configure deployment management authorization for all systems in the grid.
     services.private-storage.deployment = {
@@ -33,11 +34,12 @@ let
     imports = [
       gridlib.issuer
       gridlib.hardware-aws
-      (gridlib.customize-issuer (grid-config // {
-        monitoringvpnIPv4 = "172.23.23.11";
-      }))
+      (gridlib.customize-issuer grid-config)
       grid-module
     ];
+    config = {
+      grid.monitoringvpnIPv4 = "172.23.23.11";
+    };
   };
 
   storage001 = {
@@ -46,11 +48,13 @@ let
       gridlib.hardware-aws
       ./testing001-hardware.nix
       (gridlib.customize-storage (grid-config // {
-        monitoringvpnIPv4 = "172.23.23.12";
         stateVersion = "19.03";
       }))
       grid-module
     ];
+    config = {
+      grid.monitoringvpnIPv4 = "172.23.23.12";
+    };
   };
 
   monitoring = {
@@ -65,11 +69,13 @@ let
         inherit (grid-config) letsEncryptAdminEmail monitoringDomains;
         googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
         enableSlackAlert = true;
-        monitoringvpnIPv4 = "172.23.23.1";
         stateVersion = "19.09";
       })
       grid-module
     ];
+    config = {
+      grid.monitoringvpnIPv4 = "172.23.23.1";
+    };
   };
 
   # TBD: derive these automatically:
diff --git a/morph/lib/base.nix b/morph/lib/base.nix
index 7390654ac167909149b0a6f4dfae897b8f3f43a3..9b698b9629c12c7343fbd2e9eea5d2ee599b612e 100644
--- a/morph/lib/base.nix
+++ b/morph/lib/base.nix
@@ -18,6 +18,18 @@
       corresponding private keys for the system.
       '';
     };
+    monitoringvpnIPv4 = lib.mkOption {
+      type = lib.types.str;
+      description = ''
+        The IPv4 address of this node on the monitoring VPN.
+      '';
+    };
+    monitoringvpnEndpoint = lib.mkOption {
+      type = lib.types.str;
+      description = ''
+        The domain name and port of the monitoring VPN endpoint.
+      '';
+    };
   };
 
   # Any extra NixOS modules to load on all our servers.  Note that just
diff --git a/morph/lib/customize-issuer.nix b/morph/lib/customize-issuer.nix
index 0686556cdf6abe79f0ac9e16586c9c219f3cddb1..5a34db1ac4aa1950275404fd45154e255bcc0c42 100644
--- a/morph/lib/customize-issuer.nix
+++ b/morph/lib/customize-issuer.nix
@@ -1,16 +1,9 @@
 # Define a function which returns a value which fills in all the holes left by
 # ``issuer.nix``.
 {
-  # A string giving the IP address and port number (":"-separated) of the VPN
-  # server.
-  monitoringvpnEndpoint
-
-  # A string giving the VPN IPv4 address for this system.
-, monitoringvpnIPv4
-
   # A string giving an email address to use for Let's Encrypt registration and
   # certificate issuance.
-, letsEncryptAdminEmail
+  letsEncryptAdminEmail
 
   # A list of strings giving the domain names that point at this issuer
   # system.  These will all be included in Let's Encrypt certificate.
@@ -22,28 +15,7 @@
 , ...
 }:
 { config, ... }:
-let
-  inherit (config.grid) publicKeyPath privateKeyPath;
-in {
-  deployment.secrets = {
-    # ``.../monitoringvpn`` is a path on the deployment system of a directory
-    # containing a number of VPN-related secrets.  This is expected to contain
-    # a number of files named like ``<VPN IPv4 address>.key`` containing the
-    # VPN private key for the corresponding host.  It must also contain
-    # ``server.pub`` and ``preshared.key`` holding the VPN server's public key
-    # and the pre-shared key, respectively.  All of these things are used as
-    # the sources of various VPN-related morph secrets.
-    "monitoringvpn-secret-key".source = "${privateKeyPath}/monitoringvpn/${monitoringvpnIPv4}.key";
-    "monitoringvpn-preshared-key".source = "${privateKeyPath}/monitoringvpn/preshared.key";
-  };
-
-  services.private-storage.monitoring.vpn.client = {
-    enable = true;
-    ip = monitoringvpnIPv4;
-    endpoint = monitoringvpnEndpoint;
-    endpointPublicKeyFile = "${publicKeyPath}/monitoringvpn/server.pub";
-  };
-
+{
   services.private-storage-issuer = {
     inherit letsEncryptAdminEmail allowedChargeOrigins;
     domains = issuerDomains;
diff --git a/morph/lib/customize-monitoring.nix b/morph/lib/customize-monitoring.nix
index 2899d9940d4309b81a31f96590f0d3df1d632dc4..a8f96f2d7e11473c707b44dba1b923ad92e11799 100644
--- a/morph/lib/customize-monitoring.nix
+++ b/morph/lib/customize-monitoring.nix
@@ -9,7 +9,6 @@
   hostsMap
 
   # See ``customize-issuer.nix``.
-, monitoringvpnIPv4
 , letsEncryptAdminEmail
 , monitoringDomains
 
@@ -47,7 +46,7 @@
 }:
 { config, ... }:
 let
-  inherit (config.grid) publicKeyPath privateKeyPath;
+  inherit (config.grid) publicKeyPath privateKeyPath monitoringvpnIPv4;
 in {
   deployment.secrets = let
     # When Grafana SSO is disabled there is not necessarily any client secret
@@ -93,12 +92,8 @@ in {
           action = ["sudo" "systemctl" "restart" "grafana.service"];
         };
       };
-    monitoringvpn = {
-      "monitoringvpn-private-key".source = "${privateKeyPath}/monitoringvpn/server.key";
-      "monitoringvpn-preshared-key".source = "${privateKeyPath}/monitoringvpn/preshared.key";
-    };
     in
-      grafanaSSO // grafanaSlackUrl // monitoringvpn;
+      grafanaSSO // grafanaSlackUrl;
 
   networking.hosts = hostsMap;
 
diff --git a/morph/lib/customize-storage.nix b/morph/lib/customize-storage.nix
index 6a288213c3f117309b697e44304be9a7d5620bcb..e6b93fefdc735dfb08e3ce091ee1d125d7031b04 100644
--- a/morph/lib/customize-storage.nix
+++ b/morph/lib/customize-storage.nix
@@ -1,12 +1,8 @@
 # Define a function which returns a value which fills in all the holes left by
 # ``storage.nix``.
 {
-  # See ``customize-issuer.nix``
-  monitoringvpnEndpoint
-, monitoringvpnIPv4
-
   # An integer giving the value of a single pass in byte×months.
-, passValue
+  passValue
 
   # An integer giving the port number to include in Tahoe storage service
   # advertisements and on which to listen for storage connections.
@@ -20,21 +16,9 @@
 let
   inherit (config.grid) publicKeyPath privateKeyPath;
 in {
-  deployment.secrets = {
-    "monitoringvpn-secret-key".source = "${privateKeyPath}/monitoringvpn/${monitoringvpnIPv4}.key";
-    "monitoringvpn-preshared-key".source = "${privateKeyPath}/monitoringvpn/preshared.key";
-  };
-
   services.private-storage = {
     inherit passValue publicStoragePort;
   };
 
-  services.private-storage.monitoring.vpn.client = {
-    enable = true;
-    ip = monitoringvpnIPv4;
-    endpoint = monitoringvpnEndpoint;
-    endpointPublicKeyFile = "${publicKeyPath}/monitoringvpn/server.pub";
-  };
-
   system.stateVersion = stateVersion;
 }
diff --git a/morph/lib/issuer.nix b/morph/lib/issuer.nix
index d3ee812e865f741b01eb811589262ae01ece824f..5390f9e12ba978d20869cc735f9c5b0a217afdd0 100644
--- a/morph/lib/issuer.nix
+++ b/morph/lib/issuer.nix
@@ -6,7 +6,7 @@
 # See https://whetstone.privatestorage.io/privatestorage/PrivateStorageio/-/issues/80
 { config, ...}:
 let
-  inherit (config.grid) publicKeyPath privateKeyPath;
+  inherit (config.grid) publicKeyPath privateKeyPath monitoringvpnEndpoint monitoringvpnIPv4;
 in {
   deployment = {
     secrets = {
@@ -29,6 +29,7 @@ in {
 
       "monitoringvpn-secret-key" = {
         destination = "/run/keys/monitoringvpn/client.key";
+        source = "${privateKeyPath}/monitoringvpn/${monitoringvpnIPv4}.key";
         owner.user = "root";
         owner.group = "root";
         permissions = "0400";
@@ -36,6 +37,7 @@ in {
       };
       "monitoringvpn-preshared-key" = {
         destination = "/run/keys/monitoringvpn/preshared.key";
+        source = "${privateKeyPath}/monitoringvpn/preshared.key";
         owner.user = "root";
         owner.group = "root";
         permissions = "0400";
@@ -57,4 +59,12 @@ in {
     database = "SQLite3";
     databasePath = "${config.fileSystems."zkapissuer-data".mountPoint}/vouchers.sqlite3";
   };
+
+  services.private-storage.monitoring.vpn.client = {
+    enable = true;
+    ip = monitoringvpnIPv4;
+    endpoint = monitoringvpnEndpoint;
+    endpointPublicKeyFile = "${publicKeyPath}/monitoringvpn/server.pub";
+  };
+
 }
diff --git a/morph/lib/monitoring.nix b/morph/lib/monitoring.nix
index 89a328e89a799b445dff7180dff552350b9629cf..e10dd31c4dee7404d436285ad80c9babc350c246 100644
--- a/morph/lib/monitoring.nix
+++ b/morph/lib/monitoring.nix
@@ -1,10 +1,14 @@
 # Similar to ``issuer.nix`` but for a "monitoring"-type system.  Holes are
 # filled by ``customize-monitoring.nix``.
-{
+{ config, ...}:
+let
+  inherit (config.grid) privateKeyPath;
+in {
   deployment = {
     secrets = {
       "monitoringvpn-private-key" = {
         destination = "/run/keys/monitoringvpn/server.key";
+        source = "${privateKeyPath}/monitoringvpn/server.key";
         owner.user = "root";
         owner.group = "root";
         permissions = "0400";
@@ -12,6 +16,7 @@
       };
       "monitoringvpn-preshared-key" = {
         destination = "/run/keys/monitoringvpn/preshared.key";
+        source = "${privateKeyPath}/monitoringvpn/preshared.key";
         owner.user = "root";
         owner.group = "root";
         permissions = "0400";
diff --git a/morph/lib/storage.nix b/morph/lib/storage.nix
index 15e2373737a7ff2f1efe8cf2c41b59de606f0a1a..74ed448ac5e7d821cca0c09910a61343d2ec94af 100644
--- a/morph/lib/storage.nix
+++ b/morph/lib/storage.nix
@@ -2,7 +2,7 @@
 # by ``customize-storage.nix``.
 { config, ...} :
 let
-  inherit (config.grid) publicKeyPath privateKeyPath;
+  inherit (config.grid) publicKeyPath privateKeyPath monitoringvpnIPv4 monitoringvpnEndpoint;
 in {
   deployment = {
     secrets = {
@@ -19,6 +19,7 @@ in {
       };
       "monitoringvpn-secret-key" = {
         destination = "/run/keys/monitoringvpn/client.key";
+        source = "${privateKeyPath}/monitoringvpn/${monitoringvpnIPv4}.key";
         owner.user = "root";
         owner.group = "root";
         permissions = "0400";
@@ -26,6 +27,7 @@ in {
       };
       "monitoringvpn-preshared-key" = {
         destination = "/run/keys/monitoringvpn/preshared.key";
+        source = "${privateKeyPath}/monitoringvpn/preshared.key";
         owner.user = "root";
         owner.group = "root";
         permissions = "0400";
@@ -56,4 +58,11 @@ in {
     # Give it the Ristretto signing key to support authorization.
     ristrettoSigningKeyPath = config.deployment.secrets.ristretto-signing-key.destination;
   };
+
+  services.private-storage.monitoring.vpn.client = {
+    enable = true;
+    ip = monitoringvpnIPv4;
+    endpoint = monitoringvpnEndpoint;
+    endpointPublicKeyFile = "${publicKeyPath}/monitoringvpn/server.pub";
+  };
 }