diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index dbbc8c7f670..a77550a5153 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -559,6 +559,7 @@
./system/boot/stage-1.nix
./system/boot/stage-2.nix
./system/boot/systemd.nix
+ ./system/boot/systemd-nspawn.nix
./system/boot/timesyncd.nix
./system/boot/tmp.nix
./system/etc/etc.nix
diff --git a/nixos/modules/system/boot/systemd-nspawn.nix b/nixos/modules/system/boot/systemd-nspawn.nix
new file mode 100644
index 00000000000..2527ab35719
--- /dev/null
+++ b/nixos/modules/system/boot/systemd-nspawn.nix
@@ -0,0 +1,121 @@
+{ config, lib , pkgs, ...}:
+
+with lib;
+with import ./systemd-unit-options.nix { inherit config lib; };
+with import ./systemd-lib.nix { inherit config lib pkgs; };
+
+let
+ cfg = config.systemd.nspawn;
+ assertions = [
+ # boot = true -> processtwo != true
+ ];
+
+ checkExec = checkUnitConfig "Exec" [
+ (assertOnlyFields [
+ "Boot" "ProcessTwo" "Parameters" "Environment" "User" "WorkingDirectory"
+ "Capability" "DropCapability" "KillSignal" "Personality" "MachineId"
+ "PrivateUsers"
+ ])
+ (assertValueOneOf "Boot" boolValues)
+ (assertValueOneOf "ProcessTwo" boolValues)
+ (assertValueOneOf "PrivateUsers" (boolValues ++ [ "pick" ]))
+ ];
+
+ checkFiles = checkUnitConfig "Files" [
+ (assertOnlyFields [
+ "ReadOnly" "Volatile" "Bind" "BindReadOnly" "TemporaryFileSystems"
+ "PrivateUsersChown"
+ ])
+ (assertValueOneOf "ReadOnly" boolValues)
+ (assertValueOneOf "Volatile" (boolValues ++ [ "state" ]))
+ (assertValueOneOf "PrivateUsersChown" boolValues)
+ ];
+
+ checkNetwork = checkUnitConfig "Network" [
+ (assertOnlyFields [
+ "Private" "VirtualEthernet" "VirtualEthernetExtra" "Interface" "MACVLAN"
+ "IPVLAN" "Bridge" "Zone" "Port"
+ ])
+ (assertValueOneOf "Private" boolValues)
+ (assertValueOneOf "VirtualEthernet" boolValues)
+ ];
+
+ instanceOptions = {
+
+ execConfig = mkOption {
+ default = {};
+ example = { Parameters = "/bin/sh"; };
+ type = types.addCheck (types.attrsOf unitOption) checkExec;
+ description = ''
+ Each attribute in this set specifies an option in the
+ [Exec] section of this unit. See
+ systemd.nspawn
+ 5 for details.
+ '';
+ };
+
+ filesConfig = mkOption {
+ default = {};
+ example = { Bind = [ "/home/alice" ]; };
+ type = types.addCheck (types.attrsOf unitOption) checkFiles;
+ description = ''
+ Each attribute in this set specifies an option in the
+ [Files] section of this unit. See
+ systemd.nspawn
+ 5 for details.
+ '';
+ };
+
+ networkConfig = mkOption {
+ default = {};
+ example = { Private = false; };
+ type = types.addCheck (types.attrsOf unitOption) checkNetwork;
+ description = ''
+ Each attribute in this set specifies an option in the
+ [Network] section of this unit. See
+ systemd.nspawn
+ 5 for details.
+ '';
+ };
+
+ };
+
+ instanceToUnit = name: def:
+ { text = ''
+ [Exec]
+ ${attrsToSection def.execConfig}
+
+ [Files]
+ ${attrsToSection def.filesConfig}
+
+ [Network]
+ ${attrsToSection def.networkConfig}
+ '';
+ };
+
+in {
+
+ options = {
+
+ systemd.nspawn = mkOption {
+ default = {};
+ type = types.attrsOf types.optionSet;
+ options = [ instanceOptions ];
+ description = "Definition of systemd-nspawn configurations.";
+ };
+
+ };
+
+ config =
+ let
+ units = mapAttrs' (n: v: nameValuePair "${n}.nspawn" (instanceToUnit n v)) cfg.instances;
+ in mkIf (cfg != {}) {
+
+ environment.etc."systemd/nspawn".source = generateUnits "nspawn" units [] [];
+
+ systemd.services."systemd-nspawn@" = {
+ wantedBy = [ "machine.target" ];
+ };
+ };
+
+}