summary refs log tree commit diff
path: root/nixos/modules/virtualisation/containers.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/virtualisation/containers.nix')
-rw-r--r--nixos/modules/virtualisation/containers.nix137
1 files changed, 137 insertions, 0 deletions
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
new file mode 100644
index 00000000000..bcbfaacd703
--- /dev/null
+++ b/nixos/modules/virtualisation/containers.nix
@@ -0,0 +1,137 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+
+    boot.isContainer = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether this NixOS machine is a lightweight container running
+        in another NixOS system.
+      '';
+    };
+
+    systemd.containers = mkOption {
+      type = types.attrsOf (types.submodule (
+        { config, options, name, ... }:
+        {
+          options = {
+
+            root = mkOption {
+              type = types.path;
+              description = ''
+                The root directory of the container.
+              '';
+            };
+
+            config = mkOption {
+              description = ''
+                A specification of the desired configuration of this
+                container, as a NixOS module.
+              '';
+            };
+
+            path = mkOption {
+              type = types.path;
+              example = "/nix/var/nix/profiles/containers/webserver";
+              description = ''
+                As an alternative to specifying
+                <option>config</option>, you can specify the path to
+                the evaluated NixOS system configuration, typically a
+                symlink to a system profile.
+              '';
+            };
+
+          };
+
+          config = mkMerge
+            [ { root = mkDefault "/var/lib/containers/${name}";
+              }
+              (mkIf options.config.isDefined {
+                path = (import ../../lib/eval-config.nix {
+                  modules =
+                    let extraConfig =
+                      { boot.isContainer = true;
+                        security.initialRootPassword = "!";
+                        networking.hostName = mkDefault name;
+                      };
+                    in [ extraConfig config.config ];
+                  prefix = [ "systemd" "containers" name ];
+                }).config.system.build.toplevel;
+              })
+            ];
+        }));
+
+      default = {};
+      example = literalExample
+        ''
+          { webserver =
+              { root = "/containers/webserver";
+                path = "/nix/var/nix/profiles/webserver";
+              };
+            database =
+              { root = "/containers/database";
+                config =
+                  { config, pkgs, ... }:
+                  { services.postgresql.enable = true;
+                    services.postgresql.package = pkgs.postgresql92;
+                  };
+              };
+          }
+        '';
+      description = ''
+        A set of NixOS system configurations to be run as lightweight
+        containers.  Each container appears as a service
+        <literal>container-<replaceable>name</replaceable></literal>
+        on the host system, allowing it to be started and stopped via
+        <command>systemctl</command> .
+      '';
+    };
+
+  };
+
+
+  config = {
+
+    systemd.services = mapAttrs' (name: container: nameValuePair "container-${name}"
+      { description = "Container '${name}'";
+
+        wantedBy = [ "multi-user.target" ];
+
+        unitConfig.RequiresMountsFor = [ container.root ];
+
+        preStart =
+          ''
+            mkdir -p -m 0755 ${container.root}/etc
+            if ! [ -e ${container.root}/etc/os-release ]; then
+              touch ${container.root}/etc/os-release
+            fi
+          '';
+
+        serviceConfig.ExecStart =
+          "${config.systemd.package}/bin/systemd-nspawn -M ${name} -D ${container.root} --bind-ro=/nix ${container.path}/init";
+
+        preStop =
+          ''
+            pid="$(cat /sys/fs/cgroup/systemd/machine/${name}.nspawn/system/tasks 2> /dev/null)"
+            if [ -n "$pid" ]; then
+              # Send the RTMIN+3 signal, which causes the container
+              # systemd to start halt.target.
+              echo "killing container systemd, PID = $pid"
+              kill -RTMIN+3 $pid
+              # Wait for the container to exit.  We can't let systemd
+              # do this because it will send a signal to the entire
+              # cgroup.
+              for ((n = 0; n < 180; n++)); do
+                if ! kill -0 $pid 2> /dev/null; then break; fi
+                sleep 1
+              done
+            fi
+          '';
+      }) config.systemd.containers;
+
+  };
+}
\ No newline at end of file