diff --git a/morph/grid/local/grid.nix b/morph/grid/local/grid.nix
index 8a25747ccdb61fa2071538f706bae4ab366dff14..6718f46ada4073ae3edd45d6a9c42fcaa1813800 100644
--- a/morph/grid/local/grid.nix
+++ b/morph/grid/local/grid.nix
@@ -1,12 +1,13 @@
 # Load the helper function and call it with arguments tailored for the local
 # grid.  It will make the morph configuration for us.  We share this function
 # with the production grid and have one fewer possible point of divergence.
-import ../../lib/make-grid.nix {
+import ../../lib/make-grid.nix rec {
   name = "LocalDev";
   config = ./config.json;
   nodes = cfg:
   let
     sshUsers = import ../../../../PrivateStorageSecrets/localdev-users.nix;
+    lib = <nixpkgs/lib>;
   in {
     "payments1" = import ../../lib/make-issuer.nix (rec {
       publicIPv4 = "192.168.67.21";
@@ -35,6 +36,7 @@ import ../../lib/make-grid.nix {
     "monitoring1" = import ../../lib/make-monitoring.nix (rec {
       publicIPv4 = "192.168.67.24";
       monitoringvpnIPv4 = "172.23.23.1";
+      vpnClientIPs = builtins.filter (x: x != null) (map (x: lib.attrByPath (lib.splitString "." "services.private-storage.monitoring.vpn.client.ip") null x) (builtins.attrValues nodes));
       inherit sshUsers;
       hardware = import ./virtual-hardware.nix ({ inherit publicIPv4; });
       stateVersion = "19.09";
diff --git a/morph/lib/make-monitoring.nix b/morph/lib/make-monitoring.nix
index 0d8ad9e1468caf5715ea1d5769a8e7b1c9782ea9..ca8dfc913c0b4f05999374cf66a537cb7d1e1763 100644
--- a/morph/lib/make-monitoring.nix
+++ b/morph/lib/make-monitoring.nix
@@ -1,4 +1,4 @@
-{ publicIPv4, hardware, publicStoragePort, ristrettoSigningKeyPath, passValue, sshUsers, stateVersion, monitoringvpnIPv4, ... }: rec {
+{ publicIPv4, hardware, publicStoragePort, ristrettoSigningKeyPath, passValue, sshUsers, stateVersion, monitoringvpnIPv4, vpnClientIPs, ... }: rec {
 
   deployment = {
     targetHost = publicIPv4;
@@ -39,6 +39,7 @@
   services.private-storage.monitoring.vpn.server = {
     enable = true;
     ip = monitoringvpnIPv4;
+    inherit vpnClientIPs;
   };
 
   system.stateVersion = stateVersion;
diff --git a/nixos/modules/monitoring/vpn/server.nix b/nixos/modules/monitoring/vpn/server.nix
index 56ecf197527482bdbae4df4a88ddf9696a277bb0..0ff1189b248dfd018ff9735cac4d1c13435d29a8 100644
--- a/nixos/modules/monitoring/vpn/server.nix
+++ b/nixos/modules/monitoring/vpn/server.nix
@@ -45,6 +45,13 @@ in {
         The UDP port to listen on.
       '';
     };
+    vpnClientIPs = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      example = lib.literalExample [ "172.23.23.23" "172.23.23.42" ];
+      description = ''
+        The IP addresses to allow connections from.
+      '';
+    };
   };
 
   config = lib.mkIf cfg.server.enable {
@@ -54,23 +61,7 @@ in {
       ips = [ "${cfg.server.ip}/24" ];
       listenPort = cfg.server.port;
       privateKeyFile = toString cfg.server.privateKeyFile;
-      peers = [
-        {
-          allowedIPs = [ "172.23.23.11/32" ];
-          publicKey = builtins.readFile(../../../../morph/PrivateStorageSecrets/monitoringvpn + "/172.23.23.11.pub");
-          presharedKeyFile = toString cfg.server.presharedKeyFile;
-        }
-        {
-          allowedIPs = [ "172.23.23.12/32" ];
-          publicKey = builtins.readFile(../../../../morph/PrivateStorageSecrets/monitoringvpn + "/172.23.23.12.pub");
-          presharedKeyFile = toString cfg.server.presharedKeyFile;
-        }
-        {
-          allowedIPs = [ "172.23.23.13/32" ];
-          publicKey = builtins.readFile(../../../../morph/PrivateStorageSecrets/monitoringvpn + "/172.23.23.13.pub");
-          presharedKeyFile = toString cfg.server.presharedKeyFile;
-        }
-      ];
+      peers = map (x: {allowedIPs = [ "${x}/32" ]; publicKey = builtins.readFile(../../../../morph/PrivateStorageSecrets/monitoringvpn + "/${x}.pub"); presharedKeyFile = toString cfg.server.presharedKeyFile;}) cfg.server.vpnClientIPs;
     };
   };
 }