summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/config/users-groups.nix60
-rw-r--r--nixos/modules/security/acme.nix3
-rw-r--r--nixos/modules/security/pam.nix3
-rw-r--r--nixos/modules/services/backup/bacula.nix9
-rw-r--r--nixos/modules/services/hardware/sane_extra_backends/brscan4.nix5
-rw-r--r--nixos/modules/services/logging/logcheck.nix68
-rw-r--r--nixos/modules/services/misc/rippled.nix78
-rw-r--r--nixos/modules/services/monitoring/smartd.nix3
-rw-r--r--nixos/modules/services/monitoring/ups.nix3
-rw-r--r--nixos/modules/services/network-filesystems/tahoe.nix260
-rw-r--r--nixos/modules/services/networking/i2pd.nix55
-rw-r--r--nixos/modules/services/networking/nat.nix30
-rw-r--r--nixos/modules/services/networking/openvpn.nix78
-rw-r--r--nixos/modules/services/networking/prosody.nix3
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix35
-rw-r--r--nixos/modules/services/networking/supplicant.nix203
-rw-r--r--nixos/modules/services/networking/tinc.nix170
-rw-r--r--nixos/modules/services/networking/xinetd.nix126
-rw-r--r--nixos/modules/services/web-servers/winstone.nix3
-rw-r--r--nixos/modules/services/web-servers/zope2.nix3
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix86
-rw-r--r--nixos/modules/system/boot/luksroot.nix304
-rw-r--r--nixos/modules/system/boot/networkd.nix21
-rw-r--r--nixos/modules/system/boot/systemd.nix42
-rw-r--r--nixos/modules/system/etc/etc.nix6
-rw-r--r--nixos/modules/tasks/network-interfaces.nix508
-rw-r--r--nixos/modules/virtualisation/containers.nix6
27 files changed, 1082 insertions, 1089 deletions
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index d92deb85d2a5..57e4940378ba 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -131,13 +131,12 @@ let
       };
 
       subUidRanges = mkOption {
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule subordinateUidRange);
         default = [];
         example = [
           { startUid = 1000; count = 1; }
           { startUid = 100001; count = 65534; }
         ];
-        options = [ subordinateUidRange ];
         description = ''
           Subordinate user ids that user is allowed to use.
           They are set into <filename>/etc/subuid</filename> and are used
@@ -146,13 +145,12 @@ let
       };
 
       subGidRanges = mkOption {
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule subordinateGidRange);
         default = [];
         example = [
           { startGid = 100; count = 1; }
           { startGid = 1001; count = 999; }
         ];
-        options = [ subordinateGidRange ];
         description = ''
           Subordinate group ids that user is allowed to use.
           They are set into <filename>/etc/subgid</filename> and are used
@@ -310,32 +308,36 @@ let
   };
 
   subordinateUidRange = {
-    startUid = mkOption {
-      type = types.int;
-      description = ''
-        Start of the range of subordinate user ids that user is
-        allowed to use.
-      '';
-    };
-    count = mkOption {
-      type = types.int;
-      default = 1;
-      description = ''Count of subordinate user ids'';
+    options = {
+      startUid = mkOption {
+        type = types.int;
+        description = ''
+          Start of the range of subordinate user ids that user is
+          allowed to use.
+        '';
+      };
+      count = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''Count of subordinate user ids'';
+      };
     };
   };
 
   subordinateGidRange = {
-    startGid = mkOption {
-      type = types.int;
-      description = ''
-        Start of the range of subordinate group ids that user is
-        allowed to use.
-      '';
-    };
-    count = mkOption {
-      type = types.int;
-      default = 1;
-      description = ''Count of subordinate group ids'';
+    options = {
+      startGid = mkOption {
+        type = types.int;
+        description = ''
+          Start of the range of subordinate group ids that user is
+          allowed to use.
+        '';
+      };
+      count = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''Count of subordinate group ids'';
+      };
     };
   };
 
@@ -428,7 +430,7 @@ in {
 
     users.users = mkOption {
       default = {};
-      type = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule userOpts);
       example = {
         alice = {
           uid = 1234;
@@ -444,7 +446,6 @@ in {
         Additional user accounts to be created automatically by the system.
         This can also be used to set options for root.
       '';
-      options = [ userOpts ];
     };
 
     users.groups = mkOption {
@@ -453,11 +454,10 @@ in {
         { students.gid = 1001;
           hackers = { };
         };
-      type = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule groupOpts);
       description = ''
         Additional groups to be created automatically by the system.
       '';
-      options = [ groupOpts ];
     };
 
     # FIXME: obsolete - will remove.
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 45e8f64046b0..1a2b8779e007 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -129,11 +129,10 @@ in
 
       certs = mkOption {
         default = { };
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule certOpts);
         description = ''
           Attribute set of certificates to get signed and renewed.
         '';
-        options = [ certOpts ];
         example = {
           "example.com" = {
             webroot = "/var/www/challenges/";
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 814dd21b53de..f9aa4136c8d6 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -386,8 +386,7 @@ in
 
     security.pam.services = mkOption {
       default = [];
-      type = types.loaOf types.optionSet;
-      options = [ pamOpts ];
+      type = with types; loaOf (submodule pamOpts);
       description =
         ''
           This option defines the PAM services.  A service typically
diff --git a/nixos/modules/services/backup/bacula.nix b/nixos/modules/services/backup/bacula.nix
index 8a26aae75fe9..ef8e5e55edef 100644
--- a/nixos/modules/services/backup/bacula.nix
+++ b/nixos/modules/services/backup/bacula.nix
@@ -198,8 +198,7 @@ in {
         description = ''
           This option defines director resources in Bacula File Daemon.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ directorOptions ];
+        type = with types; attrsOf (submodule directorOptions);
       };
 
       extraClientConfig = mkOption {
@@ -253,8 +252,7 @@ in {
         description = ''
           This option defines Director resources in Bacula Storage Daemon.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ directorOptions ];
+        type = with types; attrsOf (submodule directorOptions);
       };
 
       device = mkOption {
@@ -262,8 +260,7 @@ in {
         description = ''
           This option defines Device resources in Bacula Storage Daemon.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ deviceOptions ];
+        type = with types; attrsOf (submodule deviceOptions);
       };
  
       extraStorageConfig = mkOption {
diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
index 3ec74458cd29..1923addeb3ac 100644
--- a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
+++ b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
@@ -81,12 +81,11 @@ in
         { office1 = { model = "MFC-7860DW"; ip = "192.168.1.2"; };
           office2 = { model = "MFC-7860DW"; nodename = "BRW0080927AFBCE"; };
         };
-      type = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule netDeviceOpts);
       description = ''
         The list of network devices that will be registered against the brscan4
         sane backend.
       '';
-      options = [ netDeviceOpts ];
     };
   };
 
@@ -113,4 +112,4 @@ in
     ];
 
   };
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 3a85fa60fe7a..a8a214b21550 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -62,42 +62,46 @@ let
   };
 
   ignoreOptions = {
-    level = levelOption;
+    options = {
+      level = levelOption;
 
-    regex = mkOption {
-      default = "";
-      type = types.str;
-      description = ''
-        Regex specifying which log lines to ignore.
-      '';
+      regex = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Regex specifying which log lines to ignore.
+        '';
+      };
     };
   };
 
   ignoreCronOptions = {
-    user = mkOption {
-      default = "root";
-      type = types.str;
-      description = ''
-        User that runs the cronjob.
-      '';
-    };
+    options = {
+      user = mkOption {
+        default = "root";
+        type = types.str;
+        description = ''
+          User that runs the cronjob.
+        '';
+      };
 
-    cmdline = mkOption {
-      default = "";
-      type = types.str;
-      description = ''
-        Command line for the cron job. Will be turned into a regex for the logcheck ignore rule.
-      '';
-    };
+      cmdline = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Command line for the cron job. Will be turned into a regex for the logcheck ignore rule.
+        '';
+      };
 
-    timeArgs = mkOption {
-      default = null;
-      type = types.nullOr (types.str);
-      example = "02 06 * * *";
-      description = ''
-        "min hr dom mon dow" crontab time args, to auto-create a cronjob too.
-        Leave at null to not do this and just add a logcheck ignore rule.
-      '';
+      timeArgs = mkOption {
+        default = null;
+        type = types.nullOr (types.str);
+        example = "02 06 * * *";
+        description = ''
+          "min hr dom mon dow" crontab time args, to auto-create a cronjob too.
+          Leave at null to not do this and just add a logcheck ignore rule.
+        '';
+      };
     };
   };
 
@@ -180,8 +184,7 @@ in
         description = ''
           This option defines extra ignore rules.
         '';
-        type = types.loaOf types.optionSet;
-        options = [ ignoreOptions ];
+        type = with types; loaOf (submodule ignoreOptions);
       };
 
       ignoreCron = mkOption {
@@ -189,8 +192,7 @@ in
         description = ''
           This option defines extra ignore rules for cronjobs.
         '';
-        type = types.loaOf types.optionSet;
-        options = [ ignoreOptions ignoreCronOptions ];
+        type = with types; loaOf (submodule ignoreCronOptions);
       };
 
       extraGroups = mkOption {
diff --git a/nixos/modules/services/misc/rippled.nix b/nixos/modules/services/misc/rippled.nix
index c6b67e8498ca..8bcf35a8ad38 100644
--- a/nixos/modules/services/misc/rippled.nix
+++ b/nixos/modules/services/misc/rippled.nix
@@ -154,43 +154,45 @@ let
   };
 
   dbOptions = {
-    type = mkOption {
-      description = "Rippled database type.";
-      type = types.enum ["rocksdb" "nudb"];
-      default = "rocksdb";
-    };
+    options = {
+      type = mkOption {
+        description = "Rippled database type.";
+        type = types.enum ["rocksdb" "nudb"];
+        default = "rocksdb";
+      };
 
-    path = mkOption {
-      description = "Location to store the database.";
-      type = types.path;
-      default = cfg.databasePath;
-    };
+      path = mkOption {
+        description = "Location to store the database.";
+        type = types.path;
+        default = cfg.databasePath;
+      };
 
-    compression = mkOption {
-      description = "Whether to enable snappy compression.";
-      type = types.nullOr types.bool;
-      default = null;
-    };
+      compression = mkOption {
+        description = "Whether to enable snappy compression.";
+        type = types.nullOr types.bool;
+        default = null;
+      };
 
-    onlineDelete = mkOption {
-      description = "Enable automatic purging of older ledger information.";
-      type = types.addCheck (types.nullOr types.int) (v: v > 256);
-      default = cfg.ledgerHistory;
-    };
+      onlineDelete = mkOption {
+        description = "Enable automatic purging of older ledger information.";
+        type = types.addCheck (types.nullOr types.int) (v: v > 256);
+        default = cfg.ledgerHistory;
+      };
 
-    advisoryDelete = mkOption {
-      description = ''
-	If set, then require administrative RPC call "can_delete"
-	to enable online deletion of ledger records.
-      '';
-      type = types.nullOr types.bool;
-      default = null;
-    };
+      advisoryDelete = mkOption {
+        description = ''
+	        If set, then require administrative RPC call "can_delete"
+	        to enable online deletion of ledger records.
+        '';
+        type = types.nullOr types.bool;
+        default = null;
+      };
 
-    extraOpts = mkOption {
-      description = "Extra database options.";
-      type = types.lines;
-      default = "";
+      extraOpts = mkOption {
+        description = "Extra database options.";
+        type = types.lines;
+        default = "";
+      };
     };
   };
 
@@ -213,8 +215,7 @@ in
 
       ports = mkOption {
 	description = "Ports exposed by rippled";
-	type = types.attrsOf types.optionSet;
-	options = [portOptions];
+	type = with types; attrsOf (submodule portOptions);
 	default = {
 	  rpc = {
 	    port = 5005;
@@ -238,8 +239,7 @@ in
 
       nodeDb = mkOption {
 	description = "Rippled main database options.";
-	type = types.nullOr types.optionSet;
-	options = dbOptions;
+	type = with types; nullOr (submodule dbOptions);
 	default = {
 	  type = "rocksdb";
 	  extraOpts = ''
@@ -254,15 +254,13 @@ in
 
       tempDb = mkOption {
 	description = "Rippled temporary database options.";
-	type = types.nullOr types.optionSet;
-	options = dbOptions;
+	type = with types; nullOr (submodule dbOptions);
 	default = null;
       };
 
       importDb = mkOption {
 	description = "Settings for performing a one-time import.";
-	type = types.nullOr types.optionSet;
-	options = dbOptions;
+	type = with types; nullOr (submodule dbOptions);
 	default = null;
       };
 
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index 1017005226b2..f2834f288f90 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -197,8 +197,7 @@ in
       devices = mkOption {
         default = [];
         example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
-        type = types.listOf types.optionSet;
-        options = [ smartdOpts ];
+        type = with types; listOf (submodule smartdOpts);
         description = "List of devices to monitor.";
       };
 
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
index febf0c95f5bd..c4c4ed227b35 100644
--- a/nixos/modules/services/monitoring/ups.nix
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -169,8 +169,7 @@ in
           monitoring directly.  These are usually attached to serial ports,
           but USB devices are also supported.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ upsOptions ];
+        type = with types; attrsOf (submodule upsOptions);
       };
 
     };
diff --git a/nixos/modules/services/network-filesystems/tahoe.nix b/nixos/modules/services/network-filesystems/tahoe.nix
index d4b6c05e9432..f1846b963252 100644
--- a/nixos/modules/services/network-filesystems/tahoe.nix
+++ b/nixos/modules/services/network-filesystems/tahoe.nix
@@ -8,148 +8,150 @@ in
     options.services.tahoe = {
       introducers = mkOption {
         default = {};
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule {
+          options = {
+            nickname = mkOption {
+              type = types.str;
+              description = ''
+                The nickname of this Tahoe introducer.
+              '';
+            };
+            tub.port = mkOption {
+              default = 3458;
+              type = types.int;
+              description = ''
+                The port on which the introducer will listen.
+              '';
+            };
+            tub.location = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The external location that the introducer should listen on.
+
+                If specified, the port should be included.
+              '';
+            };
+            package = mkOption {
+              default = pkgs.tahoelafs;
+              defaultText = "pkgs.tahoelafs";
+              type = types.package;
+              example = literalExample "pkgs.tahoelafs";
+              description = ''
+                The package to use for the Tahoe LAFS daemon.
+              '';
+            };
+          };
+        });
         description = ''
           The Tahoe introducers.
         '';
-        options = {
-          nickname = mkOption {
-            type = types.str;
-            description = ''
-              The nickname of this Tahoe introducer.
-            '';
-          };
-          tub.port = mkOption {
-            default = 3458;
-            type = types.int;
-            description = ''
-              The port on which the introducer will listen.
-            '';
-          };
-          tub.location = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The external location that the introducer should listen on.
-
-              If specified, the port should be included.
-            '';
-          };
-          package = mkOption {
-            default = pkgs.tahoelafs;
-            defaultText = "pkgs.tahoelafs";
-            type = types.package;
-            example = literalExample "pkgs.tahoelafs";
-            description = ''
-              The package to use for the Tahoe LAFS daemon.
-            '';
-          };
-        };
       };
       nodes = mkOption {
         default = {};
-        type = types.loaOf types.optionSet;
-        description = ''
-          The Tahoe nodes.
-        '';
-        options = {
-          nickname = mkOption {
-            type = types.str;
-            description = ''
-              The nickname of this Tahoe node.
-            '';
-          };
-          tub.port = mkOption {
-            default = 3457;
-            type = types.int;
-            description = ''
-              The port on which the tub will listen.
+        type = with types; loaOf (submodule {
+          options = {
+            nickname = mkOption {
+              type = types.str;
+              description = ''
+                The nickname of this Tahoe node.
+              '';
+            };
+            tub.port = mkOption {
+              default = 3457;
+              type = types.int;
+              description = ''
+                The port on which the tub will listen.
 
-              This is the correct setting to tweak if you want Tahoe's storage
-              system to listen on a different port.
-            '';
-          };
-          tub.location = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The external location that the node should listen on.
+                This is the correct setting to tweak if you want Tahoe's storage
+                system to listen on a different port.
+              '';
+            };
+            tub.location = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The external location that the node should listen on.
 
-              This is the setting to tweak if there are multiple interfaces
-              and you want to alter which interface Tahoe is advertising.
+                This is the setting to tweak if there are multiple interfaces
+                and you want to alter which interface Tahoe is advertising.
 
-              If specified, the port should be included.
-            '';
-          };
-          web.port = mkOption {
-            default = 3456;
-            type = types.int;
-            description = ''
-              The port on which the Web server will listen.
+                If specified, the port should be included.
+              '';
+            };
+            web.port = mkOption {
+              default = 3456;
+              type = types.int;
+              description = ''
+                The port on which the Web server will listen.
 
-              This is the correct setting to tweak if you want Tahoe's WUI to
-              listen on a different port.
-            '';
-          };
-          client.introducer = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The furl for a Tahoe introducer node.
+                This is the correct setting to tweak if you want Tahoe's WUI to
+                listen on a different port.
+              '';
+            };
+            client.introducer = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The furl for a Tahoe introducer node.
 
-              Like all furls, keep this safe and don't share it.
-            '';
-          };
-          client.helper = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The furl for a Tahoe helper node.
+                Like all furls, keep this safe and don't share it.
+              '';
+            };
+            client.helper = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The furl for a Tahoe helper node.
 
-              Like all furls, keep this safe and don't share it.
-            '';
-          };
-          client.shares.needed = mkOption {
-            default = 3;
-            type = types.int;
-            description = ''
-              The number of shares required to reconstitute a file.
-            '';
-          };
-          client.shares.happy = mkOption {
-            default = 7;
-            type = types.int;
-            description = ''
-              The number of distinct storage nodes required to store
-              a file.
-            '';
-          };
-          client.shares.total = mkOption {
-            default = 10;
-            type = types.int;
-            description = ''
-              The number of shares required to store a file.
-            '';
-          };
-          storage.enable = mkEnableOption "storage service";
-          storage.reservedSpace = mkOption {
-            default = "1G";
-            type = types.str;
-            description = ''
-              The amount of filesystem space to not use for storage.
-            '';
-          };
-          helper.enable = mkEnableOption "helper service";
-          package = mkOption {
-            default = pkgs.tahoelafs;
-            defaultText = "pkgs.tahoelafs";
-            type = types.package;
-            example = literalExample "pkgs.tahoelafs";
-            description = ''
-              The package to use for the Tahoe LAFS daemon.
-            '';
+                Like all furls, keep this safe and don't share it.
+              '';
+            };
+            client.shares.needed = mkOption {
+              default = 3;
+              type = types.int;
+              description = ''
+                The number of shares required to reconstitute a file.
+              '';
+            };
+            client.shares.happy = mkOption {
+              default = 7;
+              type = types.int;
+              description = ''
+                The number of distinct storage nodes required to store
+                a file.
+              '';
+            };
+            client.shares.total = mkOption {
+              default = 10;
+              type = types.int;
+              description = ''
+                The number of shares required to store a file.
+              '';
+            };
+            storage.enable = mkEnableOption "storage service";
+            storage.reservedSpace = mkOption {
+              default = "1G";
+              type = types.str;
+              description = ''
+                The amount of filesystem space to not use for storage.
+              '';
+            };
+            helper.enable = mkEnableOption "helper service";
+            package = mkOption {
+              default = pkgs.tahoelafs;
+              defaultText = "pkgs.tahoelafs";
+              type = types.package;
+              example = literalExample "pkgs.tahoelafs";
+              description = ''
+                The package to use for the Tahoe LAFS daemon.
+              '';
+            };
           };
-        };
+        });
+        description = ''
+          The Tahoe nodes.
+        '';
       };
     };
     config = mkMerge [
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 0cbf57314c4b..926857a0ff4e 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -187,44 +187,43 @@ in
 
       outTunnels = mkOption {
         default = {};
-        type = with types; loaOf optionSet;
+        type = with types; loaOf (submodule ( 
+          { name, config, ... }: {
+            options = commonTunOpts name;
+            config = {
+              name = mkDefault name;
+            };
+          }
+        ));
         description = ''
           Connect to someone as a client and establish a local accept endpoint
         '';
-        options = [ ({ name, config, ... }: {
-          options = commonTunOpts name;
-          config = {
-            name = mkDefault name;
-          };
-        }) ];
       };
 
       inTunnels = mkOption {
         default = {};
-        type = with types; loaOf optionSet;
+        type = with types; loaOf (submodule (
+          { name, config, ... }: {
+            options = {
+              inPort = mkOption {
+                type = types.int;
+                default = 0;
+                description = "Service port. Default to the tunnel's listen port.";
+              };
+              accessList = mkOption {
+                type = with types; listOf str;
+                default = [];
+                description = "I2P nodes that are allowed to connect to this service.";
+              };
+            } // commonTunOpts name;
+            config = {
+              name = mkDefault name;
+            };
+          }
+        ));
         description = ''
           Serve something on I2P network at port and delegate requests to address inPort.
         '';
-        options = [ ({ name, config, ... }: {
-
-          options = {
-            inPort = mkOption {
-              type = types.int;
-              default = 0;
-              description = "Service port. Default to the tunnel's listen port.";
-            };
-            accessList = mkOption {
-              type = with types; listOf str;
-              default = [];
-              description = "I2P nodes that are allowed to connect to this service.";
-            };
-          } // commonTunOpts name;
-
-          config = {
-            name = mkDefault name;
-          };
-
-        }) ];
       };
     };
   };
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index 9d163e60d5ea..a0cfc8f8fb94 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -122,23 +122,23 @@ in
     };
 
     networking.nat.forwardPorts = mkOption {
-      type = types.listOf types.optionSet;
+      type = with types; listOf (submodule {
+        options = {
+          sourcePort = mkOption {
+            type = types.int;
+            example = 8080;
+            description = "Source port of the external interface";
+          };
+
+          destination = mkOption {
+            type = types.str;
+            example = "10.0.0.1:80";
+            description = "Forward tcp connection to destination ip:port";
+          };
+        };
+      });
       default = [];
       example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; } ];
-      options = {
-        sourcePort = mkOption {
-          type = types.int;
-          example = 8080;
-          description = "Source port of the external interface";
-        };
-
-        destination = mkOption {
-          type = types.str;
-          example = "10.0.0.1:80";
-          description = "Forward tcp connection to destination ip:port";
-        };
-      };
-
       description =
         ''
           List of forwarded ports from the external interface to
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
index 82173a841a3f..8ee86ea863e3 100644
--- a/nixos/modules/services/networking/openvpn.nix
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -116,52 +116,54 @@ in
         attribute name.
       '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        config = mkOption {
-          type = types.lines;
-          description = ''
-            Configuration of this OpenVPN instance.  See
-            <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-            for details.
-          '';
-        };
+          config = mkOption {
+            type = types.lines;
+            description = ''
+              Configuration of this OpenVPN instance.  See
+              <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+              for details.
+            '';
+          };
 
-        up = mkOption {
-          default = "";
-          type = types.lines;
-          description = ''
-            Shell commands executed when the instance is starting.
-          '';
-        };
+          up = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Shell commands executed when the instance is starting.
+            '';
+          };
 
-        down = mkOption {
-          default = "";
-          type = types.lines;
-          description = ''
-            Shell commands executed when the instance is shutting down.
-          '';
-        };
+          down = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Shell commands executed when the instance is shutting down.
+            '';
+          };
 
-        autoStart = mkOption {
-          default = true;
-          type = types.bool;
-          description = "Whether this OpenVPN instance should be started automatically.";
-        };
+          autoStart = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Whether this OpenVPN instance should be started automatically.";
+          };
+
+          updateResolvConf = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Use the script from the update-resolv-conf package to automatically
+              update resolv.conf with the DNS information provided by openvpn. The
+              script will be run after the "up" commands and before the "down" commands.
+            '';
+          };
 
-        updateResolvConf = mkOption {
-          default = false;
-          type = types.bool;
-          description = ''
-            Use the script from the update-resolv-conf package to automatically
-            update resolv.conf with the DNS information provided by openvpn. The
-            script will be run after the "up" commands and before the "down" commands.
-          '';
         };
 
-      };
+      });
 
     };
 
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index f82f8bfddbb7..247c4f1efb07 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -164,7 +164,7 @@ in
 
         description = "Define the virtual hosts";
 
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule vHostOpts);
 
         example = {
           myhost = {
@@ -180,7 +180,6 @@ in
           };
         };
 
-        options = [ vHostOpts ];
       };
 
       ssl = mkOption {
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 1d15a1419722..46ccf4ae62d5 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -129,7 +129,24 @@ in
       };
 
       listenAddresses = mkOption {
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule {
+          options = {
+            addr = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                Host, IPv4 or IPv6 address to listen to.
+              '';
+            };
+            port = mkOption {
+              type = types.nullOr types.int;
+              default = null;
+              description = ''
+                Port to listen to.
+              '';
+            };
+          };
+        });
         default = [];
         example = [ { addr = "192.168.3.1"; port = 22; } { addr = "0.0.0.0"; port = 64022; } ];
         description = ''
@@ -140,22 +157,6 @@ in
           NOTE: setting this option won't automatically enable given ports
           in firewall configuration.
         '';
-        options = {
-          addr = mkOption {
-            type = types.nullOr types.str;
-            default = null;
-            description = ''
-              Host, IPv4 or IPv6 address to listen to.
-            '';
-          };
-          port = mkOption {
-            type = types.nullOr types.int;
-            default = null;
-            description = ''
-              Port to listen to.
-            '';
-          };
-        };
       };
 
       passwordAuthentication = mkOption {
diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix
index e3107edcd7a9..e433ec7c5b9b 100644
--- a/nixos/modules/services/networking/supplicant.nix
+++ b/nixos/modules/services/networking/supplicant.nix
@@ -75,7 +75,107 @@ in
   options = {
 
     networking.supplicant = mkOption {
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
+        options = {
+  
+          configFile = {
+  
+            path = mkOption {
+              type = types.path;
+              example = literalExample "/etc/wpa_supplicant.conf";
+              description = ''
+                External <literal>wpa_supplicant.conf</literal> configuration file.
+                The configuration options defined declaratively within <literal>networking.supplicant</literal> have
+                precedence over options defined in <literal>configFile</literal>.
+              '';
+            };
+  
+            writable = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether the configuration file at <literal>configFile.path</literal> should be written to by
+                <literal>wpa_supplicant</literal>.
+              '';
+            };
+  
+          };
+  
+          extraConf = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              ap_scan=1
+              device_name=My-NixOS-Device
+              device_type=1-0050F204-1
+              driver_param=use_p2p_group_interface=1
+              disable_scan_offload=1
+              p2p_listen_reg_class=81
+              p2p_listen_channel=1
+              p2p_oper_reg_class=81
+              p2p_oper_channel=1
+              manufacturer=NixOS
+              model_name=NixOS_Unstable
+              model_number=2015
+            '';
+            description = ''
+              Configuration options for <literal>wpa_supplicant.conf</literal>.
+              Options defined here have precedence over options in <literal>configFile</literal>.
+              NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will
+              be world-readable in the <literal>nix-store</literal>. For sensitive information
+              use the <literal>configFile</literal> instead.
+            '';
+          };
+  
+          extraCmdArgs = mkOption {
+            type = types.str;
+            default = "";
+            example = "-e/var/run/wpa_supplicant/entropy.bin";
+            description =
+              "Command line arguments to add when executing <literal>wpa_supplicant</literal>.";
+          };
+  
+          driver = mkOption {
+            type = types.nullOr types.str;
+            default = "nl80211,wext";
+            description = "Force a specific wpa_supplicant driver.";
+          };
+  
+          bridge = mkOption {
+            type = types.str;
+            default = "";
+            description = "Name of the bridge interface that wpa_supplicant should listen at.";
+          };
+  
+          userControlled = {
+  
+            enable = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
+                This is useful for laptop users that switch networks a lot and don't want
+                to depend on a large package such as NetworkManager just to pick nearby
+                access points.
+              '';
+            };
+  
+            socketDir = mkOption {
+              type = types.str;
+              default = "/var/run/wpa_supplicant";
+              description = "Directory of sockets for controlling wpa_supplicant.";
+            };
+  
+            group = mkOption {
+              type = types.str;
+              default = "wheel";
+              example = "network";
+              description = "Members of this group can control wpa_supplicant.";
+            };
+  
+          };
+        };
+      });
 
       default = { };
 
@@ -109,107 +209,6 @@ in
         service that can be accessed through <literal>D-Bus</literal>.
       '';
 
-      options = {
-
-        configFile = {
-
-          path = mkOption {
-            type = types.path;
-            example = literalExample "/etc/wpa_supplicant.conf";
-            description = ''
-              External <literal>wpa_supplicant.conf</literal> configuration file.
-              The configuration options defined declaratively within <literal>networking.supplicant</literal> have
-              precedence over options defined in <literal>configFile</literal>.
-            '';
-          };
-
-          writable = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Whether the configuration file at <literal>configFile.path</literal> should be written to by
-              <literal>wpa_supplicant</literal>.
-            '';
-          };
-
-        };
-
-        extraConf = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            ap_scan=1
-            device_name=My-NixOS-Device
-            device_type=1-0050F204-1
-            driver_param=use_p2p_group_interface=1
-            disable_scan_offload=1
-            p2p_listen_reg_class=81
-            p2p_listen_channel=1
-            p2p_oper_reg_class=81
-            p2p_oper_channel=1
-            manufacturer=NixOS
-            model_name=NixOS_Unstable
-            model_number=2015
-          '';
-          description = ''
-            Configuration options for <literal>wpa_supplicant.conf</literal>.
-            Options defined here have precedence over options in <literal>configFile</literal>.
-            NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will
-            be world-readable in the <literal>nix-store</literal>. For sensitive information
-            use the <literal>configFile</literal> instead.
-          '';
-        };
-
-        extraCmdArgs = mkOption {
-          type = types.str;
-          default = "";
-          example = "-e/var/run/wpa_supplicant/entropy.bin";
-          description =
-            "Command line arguments to add when executing <literal>wpa_supplicant</literal>.";
-        };
-
-        driver = mkOption {
-          type = types.nullOr types.str;
-          default = "nl80211,wext";
-          description = "Force a specific wpa_supplicant driver.";
-        };
-
-        bridge = mkOption {
-          type = types.str;
-          default = "";
-          description = "Name of the bridge interface that wpa_supplicant should listen at.";
-        };
-
-        userControlled = {
-
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
-              This is useful for laptop users that switch networks a lot and don't want
-              to depend on a large package such as NetworkManager just to pick nearby
-              access points.
-            '';
-          };
-
-          socketDir = mkOption {
-            type = types.str;
-            default = "/var/run/wpa_supplicant";
-            description = "Directory of sockets for controlling wpa_supplicant.";
-          };
-
-          group = mkOption {
-            type = types.str;
-            default = "wheel";
-            example = "network";
-            description = "Members of this group can control wpa_supplicant.";
-          };
-
-        };
-
-      };
-
     };
 
   };
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index 8da0f817ae2a..b751e9dad069 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -18,94 +18,96 @@ in
 
       networks = mkOption {
         default = { };
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule {
+          options = {
+
+            extraConfig = mkOption {
+              default = "";
+              type = types.lines;
+              description = ''
+                Extra lines to add to the tinc service configuration file.
+              '';
+            };
+
+            name = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The name of the node which is used as an identifier when communicating
+                with the remote nodes in the mesh. If null then the hostname of the system
+                is used.
+              '';
+            };
+
+            ed25519PrivateKeyFile = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                Path of the private ed25519 keyfile.
+              '';
+            };
+
+            debugLevel = mkOption {
+              default = 0;
+              type = types.addCheck types.int (l: l >= 0 && l <= 5);
+              description = ''
+                The amount of debugging information to add to the log. 0 means little
+                logging while 5 is the most logging. <command>man tincd</command> for
+                more details.
+              '';
+            };
+
+            hosts = mkOption {
+              default = { };
+              type = types.loaOf types.lines;
+              description = ''
+                The name of the host in the network as well as the configuration for that host.
+                This name should only contain alphanumerics and underscores.
+              '';
+            };
+
+            interfaceType = mkOption {
+              default = "tun";
+              type = types.addCheck types.str (n: n == "tun" || n == "tap");
+              description = ''
+                The type of virtual interface used for the network connection
+              '';
+            };
+
+            listenAddress = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The ip adress to bind to.
+              '';
+            };
+
+            package = mkOption {
+              type = types.package;
+              default = pkgs.tinc_pre;
+              defaultText = "pkgs.tinc_pre";
+              description = ''
+                The package to use for the tinc daemon's binary.
+              '';
+            };
+
+            chroot = mkOption {
+              default = true;
+              type = types.bool;
+              description = ''
+                Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security.
+                The chroot is performed after all the initialization is done, after writing pid files and opening network sockets.
+
+                Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment.
+              '';
+            };
+          };
+        });
+
         description = ''
           Defines the tinc networks which will be started.
           Each network invokes a different daemon.
         '';
-        options = {
-
-          extraConfig = mkOption {
-            default = "";
-            type = types.lines;
-            description = ''
-              Extra lines to add to the tinc service configuration file.
-            '';
-          };
-
-          name = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The name of the node which is used as an identifier when communicating
-              with the remote nodes in the mesh. If null then the hostname of the system
-              is used.
-            '';
-          };
-
-          ed25519PrivateKeyFile = mkOption {
-            default = null;
-            type = types.nullOr types.path;
-            description = ''
-              Path of the private ed25519 keyfile.
-            '';
-          };
-
-          debugLevel = mkOption {
-            default = 0;
-            type = types.addCheck types.int (l: l >= 0 && l <= 5);
-            description = ''
-              The amount of debugging information to add to the log. 0 means little
-              logging while 5 is the most logging. <command>man tincd</command> for
-              more details.
-            '';
-          };
-
-          hosts = mkOption {
-            default = { };
-            type = types.loaOf types.lines;
-            description = ''
-              The name of the host in the network as well as the configuration for that host.
-              This name should only contain alphanumerics and underscores.
-            '';
-          };
-
-          interfaceType = mkOption {
-            default = "tun";
-            type = types.addCheck types.str (n: n == "tun" || n == "tap");
-            description = ''
-              The type of virtual interface used for the network connection
-            '';
-          };
-
-          listenAddress = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The ip adress to bind to.
-            '';
-          };
-
-          package = mkOption {
-            type = types.package;
-            default = pkgs.tinc_pre;
-            defaultText = "pkgs.tinc_pre";
-            description = ''
-              The package to use for the tinc daemon's binary.
-            '';
-          };
-
-          chroot = mkOption {
-            default = true;
-            type = types.bool;
-            description = ''
-              Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security.
-              The chroot is performed after all the initialization is done, after writing pid files and opening network sockets.
-
-              Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment.
-            '';
-          };
-        };
       };
     };
 
diff --git a/nixos/modules/services/networking/xinetd.nix b/nixos/modules/services/networking/xinetd.nix
index 08680b517808..b398f346b942 100644
--- a/nixos/modules/services/networking/xinetd.nix
+++ b/nixos/modules/services/networking/xinetd.nix
@@ -65,71 +65,73 @@ in
         A list of services provided by xinetd.
       '';
 
-      type = types.listOf types.optionSet;
+      type = with types; listOf (submodule ({
+
+        options = {
+
+          name = mkOption {
+            type = types.string;
+            example = "login";
+            description = "Name of the service.";
+          };
+
+          protocol = mkOption {
+            type = types.string;
+            default = "tcp";
+            description =
+              "Protocol of the service.  Usually <literal>tcp</literal> or <literal>udp</literal>.";
+          };
+
+          port = mkOption {
+            type = types.int;
+            default = 0;
+            example = 123;
+            description = "Port number of the service.";
+          };
+
+          user = mkOption {
+            type = types.string;
+            default = "nobody";
+            description = "User account for the service";
+          };
+
+          server = mkOption {
+            type = types.string;
+            example = "/foo/bin/ftpd";
+            description = "Path of the program that implements the service.";
+          };
+
+          serverArgs = mkOption {
+            type = types.string;
+            default = "";
+            description = "Command-line arguments for the server program.";
+          };
+
+          flags = mkOption {
+            type = types.string;
+            default = "";
+            description = "";
+          };
+
+          unlisted = mkOption {
+            type = types.bool;
+            default = false;
+            description = ''
+              Whether this server is listed in
+              <filename>/etc/services</filename>.  If so, the port
+              number can be omitted.
+            '';
+          };
+
+          extraConfig = mkOption {
+            type = types.string;
+            default = "";
+            description = "Extra configuration-lines added to the section of the service.";
+          };
 
-      options = {
-
-        name = mkOption {
-          type = types.string;
-          example = "login";
-          description = "Name of the service.";
-        };
-
-        protocol = mkOption {
-          type = types.string;
-          default = "tcp";
-          description =
-            "Protocol of the service.  Usually <literal>tcp</literal> or <literal>udp</literal>.";
-        };
-
-        port = mkOption {
-          type = types.int;
-          default = 0;
-          example = 123;
-          description = "Port number of the service.";
-        };
-
-        user = mkOption {
-          type = types.string;
-          default = "nobody";
-          description = "User account for the service";
-        };
-
-        server = mkOption {
-          type = types.string;
-          example = "/foo/bin/ftpd";
-          description = "Path of the program that implements the service.";
-        };
-
-        serverArgs = mkOption {
-          type = types.string;
-          default = "";
-          description = "Command-line arguments for the server program.";
-        };
-
-        flags = mkOption {
-          type = types.string;
-          default = "";
-          description = "";
-        };
-
-        unlisted = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Whether this server is listed in
-            <filename>/etc/services</filename>.  If so, the port
-            number can be omitted.
-          '';
-        };
-
-        extraConfig = mkOption {
-          type = types.string;
-          default = "";
-          description = "Extra configuration-lines added to the section of the service.";
         };
 
-      };
+      }));
 
     };
 
diff --git a/nixos/modules/services/web-servers/winstone.nix b/nixos/modules/services/web-servers/winstone.nix
index 6dab467b35ef..064ead5ce4bb 100644
--- a/nixos/modules/services/web-servers/winstone.nix
+++ b/nixos/modules/services/web-servers/winstone.nix
@@ -113,8 +113,7 @@ in {
   options = {
     services.winstone = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ winstoneOpts ];
+      type = with types; attrsOf (submodule winstoneOpts);
       description = ''
         Defines independent Winstone services, each serving one WAR-file.
       '';
diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix
index ef3cffd582ee..8a453e015577 100644
--- a/nixos/modules/services/web-servers/zope2.nix
+++ b/nixos/modules/services/web-servers/zope2.nix
@@ -74,7 +74,7 @@ in
 
     services.zope2.instances = mkOption {
       default = {};
-      type = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule zope2Opts);
       example = literalExample ''
         {
           plone01 = {
@@ -96,7 +96,6 @@ in
         }
       '';
       description = "zope2 instances to be created automaticaly by the system.";
-      options = [ zope2Opts ];
     };
   };
 
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index cae045f78c37..c3be7407d592 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -131,51 +131,51 @@ in
           to the respective devices corresponding to those partitions.
         '';
 
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule {
+          options = {
+
+            path = mkOption {
+              example = "/boot1";
+              type = types.str;
+              description = ''
+                The path to the boot directory where GRUB will be written. Generally
+                this boot path should double as an EFI path.
+              '';
+            };
+
+            efiSysMountPoint = mkOption {
+              default = null;
+              example = "/boot1/efi";
+              type = types.nullOr types.str;
+              description = ''
+                The path to the efi system mount point. Usually this is the same
+                partition as the above path and can be left as null.
+              '';
+            };
+
+            efiBootloaderId = mkOption {
+              default = null;
+              example = "NixOS-fsid";
+              type = types.nullOr types.str;
+              description = ''
+                The id of the bootloader to store in efi nvram.
+                The default is to name it NixOS and append the path or efiSysMountPoint.
+                This is only used if <literal>boot.loader.efi.canTouchEfiVariables</literal> is true.
+              '';
+            };
+
+            devices = mkOption {
+              default = [ ];
+              example = [ "/dev/sda" "/dev/sdb" ];
+              type = types.listOf types.str;
+              description = ''
+                The path to the devices which will have the GRUB MBR written.
+                Note these are typically device paths and not paths to partitions.
+              '';
+            };
 
-        options = {
-
-          path = mkOption {
-            example = "/boot1";
-            type = types.str;
-            description = ''
-              The path to the boot directory where GRUB will be written. Generally
-              this boot path should double as an EFI path.
-            '';
-          };
-
-          efiSysMountPoint = mkOption {
-            default = null;
-            example = "/boot1/efi";
-            type = types.nullOr types.str;
-            description = ''
-              The path to the efi system mount point. Usually this is the same
-              partition as the above path and can be left as null.
-            '';
-          };
-
-          efiBootloaderId = mkOption {
-            default = null;
-            example = "NixOS-fsid";
-            type = types.nullOr types.str;
-            description = ''
-              The id of the bootloader to store in efi nvram.
-              The default is to name it NixOS and append the path or efiSysMountPoint.
-              This is only used if <literal>boot.loader.efi.canTouchEfiVariables</literal> is true.
-            '';
           };
-
-          devices = mkOption {
-            default = [ ];
-            example = [ "/dev/sda" "/dev/sdb" ];
-            type = types.listOf types.str;
-            description = ''
-              The path to the devices which will have the GRUB MBR written.
-              Note these are typically device paths and not paths to partitions.
-            '';
-          };
-
-        };
+        });
       };
 
       configurationName = mkOption {
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index f2755b49f88d..1f412fe2d8f2 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -236,165 +236,165 @@ in
         <filename>/dev/mapper/<replaceable>name</replaceable></filename>.
       '';
 
-      type = types.loaOf types.optionSet;
-
-      options = { name, ... }: { options = {
-
-        name = mkOption {
-          visible = false;
-          default = name;
-          example = "luksroot";
-          type = types.str;
-          description = "Name of the unencrypted device in <filename>/dev/mapper</filename>.";
-        };
-
-        device = mkOption {
-          example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08";
-          type = types.str;
-          description = "Path of the underlying encrypted block device.";
-        };
-
-        header = mkOption {
-          default = null;
-          example = "/root/header.img";
-          type = types.nullOr types.str;
-          description = ''
-            The name of the file or block device that
-            should be used as header for the encrypted device.
-          '';
-        };
-
-        keyFile = mkOption {
-          default = null;
-          example = "/dev/sdb1";
-          type = types.nullOr types.str;
-          description = ''
-            The name of the file (can be a raw device or a partition) that
-            should be used as the decryption key for the encrypted device. If
-            not specified, you will be prompted for a passphrase instead.
-          '';
-        };
-
-        keyFileSize = mkOption {
-          default = null;
-          example = 4096;
-          type = types.nullOr types.int;
-          description = ''
-            The size of the key file. Use this if only the beginning of the
-            key file should be used as a key (often the case if a raw device
-            or partition is used as key file). If not specified, the whole
-            <literal>keyFile</literal> will be used decryption, instead of just
-            the first <literal>keyFileSize</literal> bytes.
-          '';
-        };
-
-        # FIXME: get rid of this option.
-        preLVM = mkOption {
-          default = true;
-          type = types.bool;
-          description = "Whether the luksOpen will be attempted before LVM scan or after it.";
-        };
-
-        allowDiscards = mkOption {
-          default = false;
-          type = types.bool;
-          description = ''
-            Whether to allow TRIM requests to the underlying device. This option
-            has security implications; please read the LUKS documentation before
-            activating it.
-          '';
-        };
-
-        yubikey = mkOption {
-          default = null;
-          type = types.nullOr types.optionSet;
-          description = ''
-            The options to use for this LUKS device in Yubikey-PBA.
-            If null (the default), Yubikey-PBA will be disabled for this device.
-          '';
+      type = with types; loaOf (submodule (
+        { name, ... }: { options = {
+
+          name = mkOption {
+            visible = false;
+            default = name;
+            example = "luksroot";
+            type = types.str;
+            description = "Name of the unencrypted device in <filename>/dev/mapper</filename>.";
+          };
 
-          options = {
-            twoFactor = mkOption {
-              default = true;
-              type = types.bool;
-              description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false).";
-            };
-
-            slot = mkOption {
-              default = 2;
-              type = types.int;
-              description = "Which slot on the Yubikey to challenge.";
-            };
-
-            saltLength = mkOption {
-              default = 16;
-              type = types.int;
-              description = "Length of the new salt in byte (64 is the effective maximum).";
-            };
-
-            keyLength = mkOption {
-              default = 64;
-              type = types.int;
-              description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
-            };
-
-            iterationStep = mkOption {
-              default = 0;
-              type = types.int;
-              description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
-            };
-
-            gracePeriod = mkOption {
-              default = 2;
-              type = types.int;
-              description = "Time in seconds to wait before attempting to find the Yubikey.";
-            };
-
-            ramfsMountPoint = mkOption {
-              default = "/crypt-ramfs";
-              type = types.str;
-              description = "Path where the ramfs used to update the LUKS key will be mounted during early boot.";
-            };
-
-            /* TODO: Add to the documentation of the current module:
-
-               Options related to the storing the salt.
-            */
-            storage = {
-              device = mkOption {
-                default = "/dev/sda1";
-                type = types.path;
-                description = ''
-                  An unencrypted device that will temporarily be mounted in stage-1.
-                  Must contain the current salt to create the challenge for this LUKS device.
-                '';
-              };
+          device = mkOption {
+            example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08";
+            type = types.str;
+            description = "Path of the underlying encrypted block device.";
+          };
 
-              fsType = mkOption {
-                default = "vfat";
-                type = types.str;
-                description = "The filesystem of the unencrypted device.";
-              };
+          header = mkOption {
+            default = null;
+            example = "/root/header.img";
+            type = types.nullOr types.str;
+            description = ''
+              The name of the file or block device that
+              should be used as header for the encrypted device.
+            '';
+          };
 
-              mountPoint = mkOption {
-                default = "/crypt-storage";
-                type = types.str;
-                description = "Path where the unencrypted device will be mounted during early boot.";
-              };
+          keyFile = mkOption {
+            default = null;
+            example = "/dev/sdb1";
+            type = types.nullOr types.str;
+            description = ''
+              The name of the file (can be a raw device or a partition) that
+              should be used as the decryption key for the encrypted device. If
+              not specified, you will be prompted for a passphrase instead.
+            '';
+          };
+
+          keyFileSize = mkOption {
+            default = null;
+            example = 4096;
+            type = types.nullOr types.int;
+            description = ''
+              The size of the key file. Use this if only the beginning of the
+              key file should be used as a key (often the case if a raw device
+              or partition is used as key file). If not specified, the whole
+              <literal>keyFile</literal> will be used decryption, instead of just
+              the first <literal>keyFileSize</literal> bytes.
+            '';
+          };
+
+          # FIXME: get rid of this option.
+          preLVM = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Whether the luksOpen will be attempted before LVM scan or after it.";
+          };
+
+          allowDiscards = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Whether to allow TRIM requests to the underlying device. This option
+              has security implications; please read the LUKS documentation before
+              activating it.
+            '';
+          };
 
-              path = mkOption {
-                default = "/crypt-storage/default";
-                type = types.str;
-                description = ''
-                  Absolute path of the salt on the unencrypted device with
-                  that device's root directory as "/".
-                '';
+          yubikey = mkOption {
+            default = null;
+            description = ''
+              The options to use for this LUKS device in Yubikey-PBA.
+              If null (the default), Yubikey-PBA will be disabled for this device.
+            '';
+
+            type = with types; nullOr (submodule {
+              options = {
+                twoFactor = mkOption {
+                  default = true;
+                  type = types.bool;
+                  description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false).";
+                };
+
+                slot = mkOption {
+                  default = 2;
+                  type = types.int;
+                  description = "Which slot on the Yubikey to challenge.";
+                };
+
+                saltLength = mkOption {
+                  default = 16;
+                  type = types.int;
+                  description = "Length of the new salt in byte (64 is the effective maximum).";
+                };
+
+                keyLength = mkOption {
+                  default = 64;
+                  type = types.int;
+                  description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
+                };
+
+                iterationStep = mkOption {
+                  default = 0;
+                  type = types.int;
+                  description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
+                };
+
+                gracePeriod = mkOption {
+                  default = 2;
+                  type = types.int;
+                  description = "Time in seconds to wait before attempting to find the Yubikey.";
+                };
+
+                ramfsMountPoint = mkOption {
+                  default = "/crypt-ramfs";
+                  type = types.str;
+                  description = "Path where the ramfs used to update the LUKS key will be mounted during early boot.";
+                };
+
+                /* TODO: Add to the documentation of the current module:
+
+                   Options related to the storing the salt.
+                */
+                storage = {
+                  device = mkOption {
+                    default = "/dev/sda1";
+                    type = types.path;
+                    description = ''
+                      An unencrypted device that will temporarily be mounted in stage-1.
+                      Must contain the current salt to create the challenge for this LUKS device.
+                    '';
+                  };
+
+                  fsType = mkOption {
+                    default = "vfat";
+                    type = types.str;
+                    description = "The filesystem of the unencrypted device.";
+                  };
+
+                  mountPoint = mkOption {
+                    default = "/crypt-storage";
+                    type = types.str;
+                    description = "Path where the unencrypted device will be mounted during early boot.";
+                  };
+
+                  path = mkOption {
+                    default = "/crypt-storage/default";
+                    type = types.str;
+                    description = ''
+                      Absolute path of the salt on the unencrypted device with
+                      that device's root directory as "/".
+                    '';
+                  };
+                };
               };
-            };
+            });
           };
-        };
 
-      }; };
+        }; }));
     };
 
     boot.initrd.luks.yubikeySupport = mkOption {
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 38b4b437369d..2cf6a4cca9a4 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -471,8 +471,7 @@ let
 
     addresses = mkOption {
       default = [ ];
-      type = types.listOf types.optionSet;
-      options = [ addressOptions ];
+      type = with types; listOf (submodule [ addressOptions ]);
       description = ''
         A list of address sections to be added to the unit.  See
         <citerefentry><refentrytitle>systemd.network</refentrytitle>
@@ -482,8 +481,7 @@ let
 
     routes = mkOption {
       default = [ ];
-      type = types.listOf types.optionSet;
-      options = [ routeOptions ];
+      type = with types; listOf (submodule [ routeOptions ]);
       description = ''
         A list of route sections to be added to the unit.  See
         <citerefentry><refentrytitle>systemd.network</refentrytitle>
@@ -624,35 +622,32 @@ in
 
     systemd.network.links = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ linkOptions ];
+      type = with types; attrsOf (submodule [ linkOptions ]);
       description = "Definition of systemd network links.";
     };
 
     systemd.network.netdevs = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ netdevOptions ];
+      type = with types; attrsOf (submodule [ netdevOptions ]);
       description = "Definition of systemd network devices.";
     };
 
     systemd.network.networks = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ networkOptions networkConfig ];
+      type = with types; attrsOf (submodule [ networkOptions networkConfig ]);
       description = "Definition of systemd networks.";
     };
 
     systemd.network.units = mkOption {
       description = "Definition of networkd units.";
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = { name, config, ... }:
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
         { options = concreteUnitOptions;
           config = {
             unit = mkDefault (makeUnit name config);
           };
-        };
+        }));
     };
 
   };
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 2ac041f14128..4a59d6474808 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -389,13 +389,13 @@ in
     systemd.units = mkOption {
       description = "Definition of systemd units.";
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = { name, config, ... }:
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
         { options = concreteUnitOptions;
           config = {
             unit = mkDefault (makeUnit name config);
           };
-        };
+        }));
     };
 
     systemd.packages = mkOption {
@@ -406,43 +406,37 @@ in
 
     systemd.targets = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ targetOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
       description = "Definition of systemd target units.";
     };
 
     systemd.services = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ serviceOptions unitConfig serviceConfig ];
+      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]);
       description = "Definition of systemd service units.";
     };
 
     systemd.sockets = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ socketOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]);
       description = "Definition of systemd socket units.";
     };
 
     systemd.timers = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ timerOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]);
       description = "Definition of systemd timer units.";
     };
 
     systemd.paths = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ pathOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
       description = "Definition of systemd path units.";
     };
 
     systemd.mounts = mkOption {
       default = [];
-      type = types.listOf types.optionSet;
-      options = [ mountOptions unitConfig mountConfig ];
+      type = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]);
       description = ''
         Definition of systemd mount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
@@ -452,8 +446,7 @@ in
 
     systemd.automounts = mkOption {
       default = [];
-      type = types.listOf types.optionSet;
-      options = [ automountOptions unitConfig automountConfig ];
+      type = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]);
       description = ''
         Definition of systemd automount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
@@ -600,33 +593,30 @@ in
     systemd.user.units = mkOption {
       description = "Definition of systemd per-user units.";
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = { name, config, ... }:
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
         { options = concreteUnitOptions;
           config = {
             unit = mkDefault (makeUnit name config);
           };
-        };
+        }));
     };
 
     systemd.user.services = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ serviceOptions unitConfig serviceConfig ];
+      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
       description = "Definition of systemd per-user service units.";
     };
 
     systemd.user.timers = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ timerOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
       description = "Definition of systemd per-user timer units.";
     };
 
     systemd.user.sockets = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ socketOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
       description = "Definition of systemd per-user socket units.";
     };
 
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 163f4f4106e8..dac36229408f 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -33,7 +33,6 @@ in
   options = {
 
     environment.etc = mkOption {
-      type = types.loaOf types.optionSet;
       default = {};
       example = literalExample ''
         { example-configuration-file =
@@ -47,7 +46,8 @@ in
         Set of files that have to be linked in <filename>/etc</filename>.
       '';
 
-      options = singleton ({ name, config, ... }:
+      type = with types; loaOf (submodule (
+        { name, config, ... }:
         { options = {
 
             enable = mkOption {
@@ -117,7 +117,7 @@ in
               in mkDefault (pkgs.writeText name' config.text));
           };
 
-        });
+        }));
 
     };
 
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 9042418b7234..058e79b11f96 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -97,21 +97,22 @@ let
 
   addrOpts = v:
     assert v == 4 || v == 6;
-    {
-      address = mkOption {
-        type = types.str;
-        description = ''
-          IPv${toString v} address of the interface.  Leave empty to configure the
-          interface using DHCP.
-        '';
-      };
+    { options = {
+        address = mkOption {
+          type = types.str;
+          description = ''
+            IPv${toString v} address of the interface.  Leave empty to configure the
+            interface using DHCP.
+          '';
+        };
 
-      prefixLength = mkOption {
-        type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
-        description = ''
-          Subnet mask of the interface, specified as the number of
-          bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
-        '';
+        prefixLength = mkOption {
+          type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
+          description = ''
+            Subnet mask of the interface, specified as the number of
+            bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
+          '';
+        };
       };
     };
 
@@ -141,8 +142,7 @@ let
           { address = "10.0.0.1"; prefixLength = 16; }
           { address = "192.168.1.1"; prefixLength = 24; }
         ];
-        type = types.listOf types.optionSet;
-        options = addrOpts 4;
+        type = with types; listOf (submodule (addrOpts 4));
         description = ''
           List of IPv4 addresses that will be statically assigned to the interface.
         '';
@@ -154,8 +154,7 @@ let
           { address = "fdfd:b3f0:482::1"; prefixLength = 48; }
           { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; }
         ];
-        type = types.listOf types.optionSet;
-        options = addrOpts 6;
+        type = with types; listOf (submodule (addrOpts 6));
         description = ''
           List of IPv6 addresses that will be statically assigned to the interface.
         '';
@@ -415,8 +414,7 @@ in
         <option>networking.useDHCP</option> is true, then every
         interface not listed here will be configured using DHCP.
       '';
-      type = types.loaOf types.optionSet;
-      options = [ interfaceOpts ];
+      type = with types; loaOf (submodule interfaceOpts);
     };
 
     networking.vswitches = mkOption {
@@ -434,53 +432,55 @@ in
           interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        interfaces = mkOption {
-          example = [ "eth0" "eth1" ];
-          type = types.listOf types.str;
-          description =
-            "The physical network interfaces connected by the vSwitch.";
-        };
+          interfaces = mkOption {
+            example = [ "eth0" "eth1" ];
+            type = types.listOf types.str;
+            description =
+              "The physical network interfaces connected by the vSwitch.";
+          };
 
-        controllers = mkOption {
-          type = types.listOf types.str;
-          default = [];
-          example = [ "ptcp:6653:[::1]" ];
-          description = ''
-            Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>.
-          '';
-        };
+          controllers = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = [ "ptcp:6653:[::1]" ];
+            description = ''
+              Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>.
+            '';
+          };
 
-        openFlowRules = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            actions=normal
-          '';
-          description = ''
-            OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are
-            loaded with <literal>ovs-ofctl</literal> within one atomic operation.
-          '';
-        };
+          openFlowRules = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              actions=normal
+            '';
+            description = ''
+              OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are
+              loaded with <literal>ovs-ofctl</literal> within one atomic operation.
+            '';
+          };
+
+          extraOvsctlCmds = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              set-fail-mode <switch_name> secure
+              set Bridge <switch_name> stp_enable=true
+            '';
+            description = ''
+              Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>.
+              All commands are bundled together with the operations for adding the interfaces
+              into one atomic operation.
+            '';
+          };
 
-        extraOvsctlCmds = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            set-fail-mode <switch_name> secure
-            set Bridge <switch_name> stp_enable=true
-          '';
-          description = ''
-            Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>.
-            All commands are bundled together with the operations for adding the interfaces
-            into one atomic operation.
-          '';
         };
 
-      };
+      });
 
     };
 
@@ -499,25 +499,27 @@ in
           bridge's network interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        interfaces = mkOption {
-          example = [ "eth0" "eth1" ];
-          type = types.listOf types.str;
-          description =
-            "The physical network interfaces connected by the bridge.";
-        };
+          interfaces = mkOption {
+            example = [ "eth0" "eth1" ];
+            type = types.listOf types.str;
+            description =
+              "The physical network interfaces connected by the bridge.";
+          };
+
+          rstp = mkOption {
+            example = true;
+            default = false;
+            type = types.bool;
+            description = "Whether the bridge interface should enable rstp.";
+          };
 
-        rstp = mkOption {
-          example = true;
-          default = false;
-          type = types.bool;
-          description = "Whether the bridge interface should enable rstp.";
         };
 
-      };
+      });
 
     };
 
@@ -538,65 +540,66 @@ in
         name specifying the name of the bond's network interface
       '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        interfaces = mkOption {
-          example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
-          type = types.listOf types.str;
-          description = "The interfaces to bond together";
-        };
+          interfaces = mkOption {
+            example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
+            type = types.listOf types.str;
+            description = "The interfaces to bond together";
+          };
 
-        lacp_rate = mkOption {
-          default = null;
-          example = "fast";
-          type = types.nullOr types.str;
-          description = ''
-            Option specifying the rate in which we'll ask our link partner
-            to transmit LACPDU packets in 802.3ad mode.
-          '';
-        };
-
-        miimon = mkOption {
-          default = null;
-          example = 100;
-          type = types.nullOr types.int;
-          description = ''
-            Miimon is the number of millisecond in between each round of polling
-            by the device driver for failed links. By default polling is not
-            enabled and the driver is trusted to properly detect and handle
-            failure scenarios.
-          '';
-        };
-
-        mode = mkOption {
-          default = null;
-          example = "active-backup";
-          type = types.nullOr types.str;
-          description = ''
-            The mode which the bond will be running. The default mode for
-            the bonding driver is balance-rr, optimizing for throughput.
-            More information about valid modes can be found at
-            https://www.kernel.org/doc/Documentation/networking/bonding.txt
-          '';
-        };
+          lacp_rate = mkOption {
+            default = null;
+            example = "fast";
+            type = types.nullOr types.str;
+            description = ''
+              Option specifying the rate in which we'll ask our link partner
+              to transmit LACPDU packets in 802.3ad mode.
+            '';
+          };
+
+          miimon = mkOption {
+            default = null;
+            example = 100;
+            type = types.nullOr types.int;
+            description = ''
+              Miimon is the number of millisecond in between each round of polling
+              by the device driver for failed links. By default polling is not
+              enabled and the driver is trusted to properly detect and handle
+              failure scenarios.
+            '';
+          };
+
+          mode = mkOption {
+            default = null;
+            example = "active-backup";
+            type = types.nullOr types.str;
+            description = ''
+              The mode which the bond will be running. The default mode for
+              the bonding driver is balance-rr, optimizing for throughput.
+              More information about valid modes can be found at
+              https://www.kernel.org/doc/Documentation/networking/bonding.txt
+            '';
+          };
+
+          xmit_hash_policy = mkOption {
+            default = null;
+            example = "layer2+3";
+            type = types.nullOr types.str;
+            description = ''
+              Selects the transmit hash policy to use for slave selection in
+              balance-xor, 802.3ad, and tlb modes.
+            '';
+          };
 
-        xmit_hash_policy = mkOption {
-          default = null;
-          example = "layer2+3";
-          type = types.nullOr types.str;
-          description = ''
-            Selects the transmit hash policy to use for slave selection in
-            balance-xor, 802.3ad, and tlb modes.
-          '';
         };
 
-      };
+      });
     };
 
     networking.macvlans = mkOption {
-      type = types.attrsOf types.optionSet;
       default = { };
       example = literalExample {
         wan = {
@@ -608,26 +611,28 @@ in
         This option allows you to define macvlan interfaces which should
         be automatically created.
       '';
-      options = {
+      type = with types; attrsOf (submodule {
+        options = {
+
+          interface = mkOption {
+            example = "enp4s0";
+            type = types.str;
+            description = "The interface the macvlan will transmit packets through.";
+          };
+
+          mode = mkOption {
+            default = null;
+            type = types.nullOr types.str;
+            example = "vepa";
+            description = "The mode of the macvlan device.";
+          };
 
-        interface = mkOption {
-          example = "enp4s0";
-          type = types.str;
-          description = "The interface the macvlan will transmit packets through.";
         };
 
-        mode = mkOption {
-          default = null;
-          type = types.nullOr types.str;
-          example = "vepa";
-          description = "The mode of the macvlan device.";
-        };
-
-      };
+      });
     };
 
     networking.sits = mkOption {
-      type = types.attrsOf types.optionSet;
       default = { };
       example = literalExample {
         hurricane = {
@@ -644,46 +649,49 @@ in
       description = ''
         This option allows you to define 6-to-4 interfaces which should be automatically created.
       '';
-      options = {
-
-        remote = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "10.0.0.1";
-          description = ''
-            The address of the remote endpoint to forward traffic over.
-          '';
-        };
-
-        local = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "10.0.0.22";
-          description = ''
-            The address of the local endpoint which the remote
-            side should send packets to.
-          '';
-        };
-
-        ttl = mkOption {
-          type = types.nullOr types.int;
-          default = null;
-          example = 255;
-          description = ''
-            The time-to-live of the connection to the remote tunnel endpoint.
-          '';
-        };
+      type = with types; attrsOf (submodule {
+        options = {
+
+          remote = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "10.0.0.1";
+            description = ''
+              The address of the remote endpoint to forward traffic over.
+            '';
+          };
+
+          local = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "10.0.0.22";
+            description = ''
+              The address of the local endpoint which the remote
+              side should send packets to.
+            '';
+          };
+
+          ttl = mkOption {
+            type = types.nullOr types.int;
+            default = null;
+            example = 255;
+            description = ''
+              The time-to-live of the connection to the remote tunnel endpoint.
+            '';
+          };
+
+          dev = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "enp4s0f0";
+            description = ''
+              The underlying network device on which the tunnel resides.
+            '';
+          };
 
-        dev = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "enp4s0f0";
-          description = ''
-            The underlying network device on which the tunnel resides.
-          '';
         };
 
-      };
+      });
     };
 
     networking.vlans = mkOption {
@@ -706,23 +714,26 @@ in
           specifying the name of the vlan interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        id = mkOption {
-          example = 1;
-          type = types.int;
-          description = "The vlan identifier";
-        };
+          id = mkOption {
+            example = 1;
+            type = types.int;
+            description = "The vlan identifier";
+          };
+
+          interface = mkOption {
+            example = "enp4s0";
+            type = types.str;
+            description = "The interface the vlan will transmit packets through.";
+          };
 
-        interface = mkOption {
-          example = "enp4s0";
-          type = types.str;
-          description = "The interface the vlan will transmit packets through.";
         };
 
-      };
+      });
+
     };
 
     networking.wlanInterfaces = mkOption {
@@ -760,73 +771,76 @@ in
           would have to be created explicitly.
         '';
 
-      type = types.attrsOf types.optionSet;
-
-      options = {
+      type = with types; attrsOf (submodule {
 
-        device = mkOption {
-          type = types.string;
-          example = "wlp6s0";
-          description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>.";
-        };
+        options = {
 
-        type = mkOption {
-          type = types.string;
-          default = "managed";
-          example = "ibss";
-          description = ''
-            The type of the WLAN interface. The type has to be either <literal>managed</literal>,
-            <literal>ibss</literal>, <literal>monitor</literal>, <literal>mesh</literal> or <literal>wds</literal>.
-            Also, the type has to be supported by the underlying hardware of the device.
-          '';
-        };
+          device = mkOption {
+            type = types.string;
+            example = "wlp6s0";
+            description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>.";
+          };
 
-        meshID = mkOption {
-          type = types.nullOr types.string;
-          default = null;
-          description = "MeshID of interface with type <literal>mesh</literal>.";
-        };
-
-        flags = mkOption {
-          type = types.nullOr types.string;
-          default = null;
-          example = "control";
-          description = ''
-            Flags for interface of type <literal>monitor</literal>. The valid flags are:
-            none:     no special flags
-            fcsfail:  show frames with FCS errors
-            control:  show control frames
-            otherbss: show frames from other BSSes
-            cook:     use cooked mode
-            active:   use active mode (ACK incoming unicast packets)
-          '';
-        };
+          type = mkOption {
+            type = types.string;
+            default = "managed";
+            example = "ibss";
+            description = ''
+              The type of the WLAN interface. The type has to be either <literal>managed</literal>,
+              <literal>ibss</literal>, <literal>monitor</literal>, <literal>mesh</literal> or <literal>wds</literal>.
+              Also, the type has to be supported by the underlying hardware of the device.
+            '';
+          };
+
+          meshID = mkOption {
+            type = types.nullOr types.string;
+            default = null;
+            description = "MeshID of interface with type <literal>mesh</literal>.";
+          };
+
+          flags = mkOption {
+            type = types.nullOr types.string;
+            default = null;
+            example = "control";
+            description = ''
+              Flags for interface of type <literal>monitor</literal>. The valid flags are:
+              none:     no special flags
+              fcsfail:  show frames with FCS errors
+              control:  show control frames
+              otherbss: show frames from other BSSes
+              cook:     use cooked mode
+              active:   use active mode (ACK incoming unicast packets)
+            '';
+          };
+
+          fourAddr = mkOption {
+            type = types.nullOr types.bool;
+            default = null;
+            description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>.";
+          };
+
+          mac = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "02:00:00:00:00:01";
+            description = ''
+              MAC address to use for the device. If <literal>null</literal>, then the MAC of the
+              underlying hardware WLAN device is used.
+
+              INFO: Locally administered MAC addresses are of the form:
+              <itemizedlist>
+              <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem>
+              </itemizedlist>
+            '';
+          };
 
-        fourAddr = mkOption {
-          type = types.nullOr types.bool;
-          default = null;
-          description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>.";
         };
 
-        mac = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "02:00:00:00:00:01";
-          description = ''
-            MAC address to use for the device. If <literal>null</literal>, then the MAC of the
-            underlying hardware WLAN device is used.
-
-            INFO: Locally administered MAC addresses are of the form:
-            <itemizedlist>
-            <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem>
-            </itemizedlist>
-          '';
-        };
+      });
 
-      };
     };
 
     networking.useDHCP = mkOption {
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 1410a1481704..b3055f49f9b2 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -473,9 +473,8 @@ in
             };
 
             extraVeths = mkOption {
-              type = types.attrsOf types.optionSet;
+              type = with types; attrsOf (submodule networkOptions);
               default = {};
-              options = networkOptions;
               description = ''
                 Extra veth-pairs to be created for the container
               '';
@@ -490,8 +489,7 @@ in
             };
 
             bindMounts = mkOption {
-              type = types.loaOf types.optionSet;
-              options = [ bindMountOpts ];
+              type = with types; loaOf (submodule bindMountOpts);
               default = {};
               example = { "/home" = { hostPath = "/home/alice";
                                       isReadOnly = false; };