diff --git a/morph/grid/local/grid.nix b/morph/grid/local/grid.nix
index efdfa1f955d9864e56acb20bb0c5810f22c47643..e672f3d14ef5f7389511a818921a9a75c6e948fe 100644
--- a/morph/grid/local/grid.nix
+++ b/morph/grid/local/grid.nix
@@ -59,7 +59,7 @@ let
     grid = {
       publicKeyPath = toString ./. + "/${grid-config.publicKeyPath}";
       privateKeyPath = toString ./. + "/${grid-config.privateKeyPath}";
-      inherit (grid-config) monitoringvpnEndpoint;
+      inherit (grid-config) monitoringvpnEndpoint letsEncryptAdminEmail;
     };
     # Configure deployment management authorization for all systems in the grid.
     services.private-storage.deployment = {
@@ -77,7 +77,7 @@ let
       grid.monitoringvpnIPv4 = "172.23.23.11";
       grid.publicIPv4 = "192.168.67.21";
       grid.issuer = {
-        inherit (grid-config) letsEncryptAdminEmail issuerDomains allowedChargeOrigins;
+        inherit (grid-config) issuerDomains allowedChargeOrigins;
       };
     };
   };
@@ -115,19 +115,18 @@ let
   monitoring = {
     imports = [
       gridlib.monitoring
-      (gridlib.customize-monitoring {
-        inherit paymentExporterTargets
-                blackboxExporterHttpsTargets;
-        inherit (grid-config) letsEncryptAdminEmail monitoringDomains;
-        googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
-        enableSlackAlert = false;
-        stateVersion = "19.09";
-      })
       grid-module
     ];
     config = {
       grid.monitoringvpnIPv4 = "172.23.23.1";
       grid.publicIPv4 = "192.168.67.24";
+      grid.monitoring = {
+        inherit paymentExporterTargets blackboxExporterHttpsTargets;
+        inherit (grid-config) monitoringDomains;
+        googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
+        enableSlackAlert = false;
+      };
+      system.stateVersion = "19.09";
     };
   };
 
diff --git a/morph/grid/production/grid.nix b/morph/grid/production/grid.nix
index 33b9726ffe7dc90a6d6528fb3d0da7d2fa401430..ab45d4ba7f67e71383d28120bd925ac3a05f04ef 100644
--- a/morph/grid/production/grid.nix
+++ b/morph/grid/production/grid.nix
@@ -21,7 +21,7 @@ let
     grid = {
       publicKeyPath = toString ./. + "/${grid-config.publicKeyPath}";
       privateKeyPath = toString ./. + "/${grid-config.privateKeyPath}";
-      inherit (grid-config) monitoringvpnEndpoint;
+      inherit (grid-config) monitoringvpnEndpoint letsEncryptAdminEmail;
     };
     # Configure deployment management authorization for all systems in the grid.
     services.private-storage.deployment = {
@@ -39,7 +39,7 @@ let
     config = {
       grid.monitoringvpnIPv4 = "172.23.23.11";
       grid.issuer = {
-        inherit (grid-config) letsEncryptAdminEmail issuerDomains allowedChargeOrigins;
+        inherit (grid-config) issuerDomains allowedChargeOrigins;
       };
     };
   };
@@ -48,18 +48,17 @@ let
     imports = [
       gridlib.monitoring
       gridlib.hardware-aws
-      (gridlib.customize-monitoring {
-        inherit paymentExporterTargets
-                blackboxExporterHttpsTargets;
-        inherit (grid-config) letsEncryptAdminEmail monitoringDomains;
-        googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
-        enableSlackAlert = true;
-        stateVersion = "19.09";
-      })
       grid-module
     ];
     config = {
       grid.monitoringvpnIPv4 = "172.23.23.1";
+      grid.monitoring = {
+        inherit paymentExporterTargets blackboxExporterHttpsTargets;
+        inherit (grid-config) monitoringDomains;
+        googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
+        enableSlackAlert = true;
+      };
+      system.stateVersion = "19.09";
     };
   };
 
diff --git a/morph/grid/testing/grid.nix b/morph/grid/testing/grid.nix
index 8d3e5483f398a5db5c422d197a80ab6f41542d15..19839ae83fa16c31adf0fcd9e3727a8304f8dd6c 100644
--- a/morph/grid/testing/grid.nix
+++ b/morph/grid/testing/grid.nix
@@ -21,7 +21,7 @@ let
     grid = {
       publicKeyPath = toString ./. + "/${grid-config.publicKeyPath}";
       privateKeyPath = toString ./. + "/${grid-config.privateKeyPath}";
-      inherit (grid-config) monitoringvpnEndpoint;
+      inherit (grid-config) monitoringvpnEndpoint letsEncryptAdminEmail;
     };
     # Configure deployment management authorization for all systems in the grid.
     services.private-storage.deployment = {
@@ -39,7 +39,7 @@ let
     config = {
       grid.monitoringvpnIPv4 = "172.23.23.11";
       grid.issuer = {
-        inherit (grid-config) letsEncryptAdminEmail issuerDomains allowedChargeOrigins;
+        inherit (grid-config) issuerDomains allowedChargeOrigins;
       };
     };
   };
@@ -64,18 +64,17 @@ let
     imports = [
       gridlib.monitoring
       gridlib.hardware-aws
-      (gridlib.customize-monitoring {
-        inherit paymentExporterTargets
-                blackboxExporterHttpsTargets;
-        inherit (grid-config) letsEncryptAdminEmail monitoringDomains;
-        googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
-        enableSlackAlert = true;
-        stateVersion = "19.09";
-      })
       grid-module
     ];
     config = {
       grid.monitoringvpnIPv4 = "172.23.23.1";
+      grid.monitoring = {
+        inherit paymentExporterTargets blackboxExporterHttpsTargets;
+        inherit (grid-config) monitoringDomains;
+        googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
+        enableSlackAlert = true;
+      };
+      system.stateVersion = "19.09";
     };
   };
 
diff --git a/morph/lib/base.nix b/morph/lib/base.nix
index 9b698b9629c12c7343fbd2e9eea5d2ee599b612e..c814631f7e04ee6c8a5206893863d6e6d6fe9e97 100644
--- a/morph/lib/base.nix
+++ b/morph/lib/base.nix
@@ -30,6 +30,14 @@
         The domain name and port of the monitoring VPN endpoint.
       '';
     };
+
+    letsEncryptAdminEmail = lib.mkOption {
+      type = lib.types.str;
+      description = ''
+        A string giving an email address to use for Let's Encrypt registration and
+        certificate issuance.
+      '';
+    };
   };
 
   # Any extra NixOS modules to load on all our servers.  Note that just
diff --git a/morph/lib/customize-monitoring.nix b/morph/lib/customize-monitoring.nix
deleted file mode 100644
index 841a0d04b9bb462eb9d62742a514c9fa412f3206..0000000000000000000000000000000000000000
--- a/morph/lib/customize-monitoring.nix
+++ /dev/null
@@ -1,101 +0,0 @@
-# Define a function which returns a value which fills in all the holes left by
-# ``monitoring.nix``.
-{
-  # A string giving an email address to use for Let's Encrypt registration and
-  # certificate issuance.
-  letsEncryptAdminEmail
-
-  # A list of strings giving the domain names that point at this monitoring
-  # system.  These will all be included in Let's Encrypt certificate.
-, monitoringDomains
-
-  # A list of VPN clients (IP addresses or hostnames) as strings indicating
-  # which nodes to scrape "nginxExporter" metrics from.
-, nginxExporterTargets ? []
-
-  # A list of VPN clients (IP addresses or hostnames) as strings indicating
-  # which nodes to scrape PaymentServer metrics from.
-, paymentExporterTargets ? []
-
-  # A list of HTTPS servers (URLs, IP addresses or hostnames) as strings indicating
-  # which nodes the BlackboxExporter should scrape HTTP and TLS metrics from.
-, blackboxExporterHttpsTargets ? []
-
-  # A string containing the GSuite OAuth2 ClientID to use to authenticate
-  # logins to Grafana.
-, googleOAuthClientID
-
-  # Whether to enable alerting via Slack.
-  # When true requires a grafana-slack-url file (see private-keys/README.rst).
-, enableSlackAlert ? false
-
-  # A string giving the NixOS state version for the system.
-, stateVersion
-, ...
-}:
-{ config, ... }:
-let
-  inherit (config.grid) publicKeyPath privateKeyPath monitoringvpnIPv4;
-in {
-  deployment.secrets = let
-    # When Grafana SSO is disabled there is not necessarily any client secret
-    # available.  Avoid telling morph that there is one in this case (so it
-    # avoids trying to read it and then failing).  Even if the secret did
-    # exist, if SSO is disabled there's no point sending the secret to the
-    # server.
-    #
-    # Also, we have to define this whole secret here so that we can configure
-    # it completely or not at all.  morph gets angry if we half configure it
-    # (say, by just omitting the "source" value).
-    grafanaSSO =
-      if googleOAuthClientID == ""
-      then { }
-      else {
-        "grafana-google-sso-secret" = {
-          source = "${privateKeyPath}/grafana-google-sso.secret";
-          destination = "/run/keys/grafana-google-sso.secret";
-          owner.user = config.systemd.services.grafana.serviceConfig.User;
-          owner.group = config.users.users.grafana.group;
-          permissions = "0400";
-          action = ["sudo" "systemctl" "restart" "grafana.service"];
-        };
-        "grafana-admin-password" = {
-          source = "${privateKeyPath}/grafana-admin.password";
-          destination = "/run/keys/grafana-admin.password";
-          owner.user = config.systemd.services.grafana.serviceConfig.User;
-          owner.group = config.users.users.grafana.group;
-          permissions = "0400";
-          action = ["sudo" "systemctl" "restart" "grafana.service"];
-        };
-      };
-    grafanaSlackUrl =
-      if !enableSlackAlert
-      then { }
-      else {
-        "grafana-slack-url" = {
-          source = "${privateKeyPath}/grafana-slack-url";
-          destination = "/run/keys/grafana-slack-url";
-          owner.user = config.systemd.services.grafana.serviceConfig.User;
-          owner.group = config.users.users.grafana.group;
-          permissions = "0400";
-          action = ["sudo" "systemctl" "restart" "grafana.service"];
-        };
-      };
-    in
-      grafanaSSO // grafanaSlackUrl;
-
-  services.private-storage.monitoring.prometheus = {
-    inherit nginxExporterTargets;
-    inherit paymentExporterTargets;
-    inherit blackboxExporterHttpsTargets;
-  };
-
-  services.private-storage.monitoring.grafana = {
-    inherit letsEncryptAdminEmail;
-    inherit googleOAuthClientID;
-    inherit enableSlackAlert;
-    domains = monitoringDomains;
-  };
-
-  system.stateVersion = stateVersion;
-}
diff --git a/morph/lib/default.nix b/morph/lib/default.nix
index 766fda5102589fbdc32e26c17fbc94731ab71a73..88c83bc2211da2e00e69f95d7d2110d3ee636cc3 100644
--- a/morph/lib/default.nix
+++ b/morph/lib/default.nix
@@ -9,9 +9,7 @@
 
   issuer = import ./issuer.nix;
   storage = import ./storage.nix;
-
   monitoring = import ./monitoring.nix;
-  customize-monitoring = import ./customize-monitoring.nix;
 
   modules = builtins.toString ../../nixos/modules;
 
diff --git a/morph/lib/issuer.nix b/morph/lib/issuer.nix
index b2d48c2cee8cb914d7c3c9acedfcaf9ad2997178..69b0527cd74e0752ded6ffbe7513db126f0613f5 100644
--- a/morph/lib/issuer.nix
+++ b/morph/lib/issuer.nix
@@ -3,7 +3,7 @@
 { lib, config, ...}:
 let
   inherit (config.grid) publicKeyPath privateKeyPath monitoringvpnEndpoint monitoringvpnIPv4;
-  inherit (config.grid.issuer) letsEncryptAdminEmail issuerDomains allowedChargeOrigins;
+  inherit (config.grid.issuer) issuerDomains allowedChargeOrigins;
 in {
   imports = [
     ../../nixos/modules/monitoring/vpn/client.nix
@@ -11,14 +11,6 @@ in {
   ];
 
   options.grid.issuer = {
-    letsEncryptAdminEmail = lib.mkOption {
-      type = lib.types.str;
-      description = ''
-        A string giving an email address to use for Let's Encrypt registration and
-        certificate issuance.
-      '';
-    };
-
     issuerDomains = lib.mkOption {
       type = lib.types.listOf lib.types.str;
       description = ''
@@ -82,7 +74,8 @@ in {
       stripeSecretKeyPath = config.deployment.secrets.stripe-secret-key.destination;
       database = "SQLite3";
       databasePath = "${config.fileSystems."zkapissuer-data".mountPoint}/vouchers.sqlite3";
-      inherit letsEncryptAdminEmail allowedChargeOrigins;
+      inherit (config.grid) letsEncryptAdminEmail;
+      inherit allowedChargeOrigins;
       domains = issuerDomains;
     };
 
diff --git a/morph/lib/monitoring.nix b/morph/lib/monitoring.nix
index ca1db1a029775b37e749b4462545dd539c66fda2..c52d19e872e9db547c4ba53df83db130da815312 100644
--- a/morph/lib/monitoring.nix
+++ b/morph/lib/monitoring.nix
@@ -1,8 +1,9 @@
-# Similar to ``issuer.nix`` but for a "monitoring"-type system.  Holes are
-# filled by ``customize-monitoring.nix``.
+# This contains all of the NixOS system configuration necessary to specify an
+# "monitoring"-type system.
 { lib, config, nodes, ...}:
 let
-  inherit (config.grid) publicKeyPath privateKeyPath monitoringvpnIPv4;
+  cfg = config.grid.monitoring;
+  inherit (config.grid) publicKeyPath privateKeyPath monitoringvpnIPv4 letsEncryptAdminEmail;
 
   # This collects information about monitored hosts from their configuration for use below.
   monitoringHosts = lib.mapAttrsToList (name: node: rec {
@@ -35,27 +36,100 @@ in {
     # ../../nixos/modules/monitoring/server/loki.nix
   ];
 
+  options.grid.monitoring = {
+    paymentExporterTargets = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      description = ''
+        A list of VPN clients (IP addresses or hostnames) as strings indicating
+        which nodes to scrape PaymentServer metrics from.
+      '';
+    };
+
+    blackboxExporterHttpsTargets = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      description = ''
+        A list of HTTPS servers (URLs, IP addresses or hostnames) as strings indicating
+        which nodes the BlackboxExporter should scrape HTTP and TLS metrics from.
+      '';
+    };
+
+    monitoringDomains = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      description = ''
+        A list of strings giving the domain names that point at this monitoring
+        system.  These will all be included in Let's Encrypt certificate.
+      '';
+    };
+
+
+    googleOAuthClientID = lib.mkOption {
+      type = lib.types.str;
+      default = "";
+      description = ''
+        A string containing the GSuite OAuth2 ClientID to use to authenticate
+        logins to Grafana.
+      '';
+    };
+
+    enableSlackAlert = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Whether to enable alerting via Slack.
+        When true requires a grafana-slack-url file (see private-keys/README.rst).
+      '';
+    };
+  };
+
   config = {
-    deployment = {
-      secrets = {
+    deployment.secrets = lib.mkMerge [
+      {
         "monitoringvpn-private-key" = {
-          destination = "/run/keys/monitoringvpn/server.key";
-          source = "${privateKeyPath}/monitoringvpn/server.key";
-          owner.user = "root";
-          owner.group = "root";
-          permissions = "0400";
-          action = ["sudo" "systemctl" "restart" "wireguard-monitoringvpn.service"];
+        destination = "/run/keys/monitoringvpn/server.key";
+        source = "${privateKeyPath}/monitoringvpn/server.key";
+        owner.user = "root";
+        owner.group = "root";
+        permissions = "0400";
+        action = ["sudo" "systemctl" "restart" "wireguard-monitoringvpn.service"];
         };
         "monitoringvpn-preshared-key" = {
-          destination = "/run/keys/monitoringvpn/preshared.key";
-          source = "${privateKeyPath}/monitoringvpn/preshared.key";
-          owner.user = "root";
-          owner.group = "root";
+        destination = "/run/keys/monitoringvpn/preshared.key";
+        source = "${privateKeyPath}/monitoringvpn/preshared.key";
+        owner.user = "root";
+        owner.group = "root";
+        permissions = "0400";
+        action = ["sudo" "systemctl" "restart" "wireguard-monitoringvpn.service"];
+        };
+      }
+      (lib.mkIf (cfg.googleOAuthClientID != "") {
+        "grafana-google-sso-secret" = {
+          source = "${privateKeyPath}/grafana-google-sso.secret";
+          destination = "/run/keys/grafana-google-sso.secret";
+          owner.user = config.systemd.services.grafana.serviceConfig.User;
+          owner.group = config.users.users.grafana.group;
           permissions = "0400";
-          action = ["sudo" "systemctl" "restart" "wireguard-monitoringvpn.service"];
+          action = ["sudo" "systemctl" "restart" "grafana.service"];
         };
-      };
-    };
+        "grafana-admin-password" = {
+          source = "${privateKeyPath}/grafana-admin.password";
+          destination = "/run/keys/grafana-admin.password";
+          owner.user = config.systemd.services.grafana.serviceConfig.User;
+          owner.group = config.users.users.grafana.group;
+          permissions = "0400";
+          action = ["sudo" "systemctl" "restart" "grafana.service"];
+        };
+      })
+      (lib.mkIf cfg.enableSlackAlert {
+        "grafana-slack-url" = {
+          source = "${privateKeyPath}/grafana-slack-url";
+          destination = "/run/keys/grafana-slack-url";
+          owner.user = config.systemd.services.grafana.serviceConfig.User;
+          owner.group = config.users.users.grafana.group;
+          permissions = "0400";
+          action = ["sudo" "systemctl" "restart" "grafana.service"];
+        };
+      })
+    ];
 
     networking.hosts = hostsMap;
 
@@ -68,6 +142,14 @@ in {
 
     services.private-storage.monitoring.prometheus = {
       inherit nodeExporterTargets;
+      inherit (cfg) paymentExporterTargets blackboxExporterHttpsTargets;
+      nginxExporterTargets = [];
+    };
+
+    services.private-storage.monitoring.grafana = {
+      inherit (cfg) googleOAuthClientID enableSlackAlert ;
+      inherit letsEncryptAdminEmail;
+      domains = cfg.monitoringDomains;
     };
   };
 }