summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorVladimír Čunát <vcunat@gmail.com>2014-09-13 21:44:45 +0200
committerVladimír Čunát <vcunat@gmail.com>2014-09-13 21:48:29 +0200
commitd957b4bd78335b845eb5d405be40fa36951b5bea (patch)
treef4e9b5320d1c83c020024e3c31d95c964d351fbd /nixos
parent6a63d1e1e107c0a7c8fe77cdc2bf62155ca88d25 (diff)
downloadnixlib-d957b4bd78335b845eb5d405be40fa36951b5bea.tar
nixlib-d957b4bd78335b845eb5d405be40fa36951b5bea.tar.gz
nixlib-d957b4bd78335b845eb5d405be40fa36951b5bea.tar.bz2
nixlib-d957b4bd78335b845eb5d405be40fa36951b5bea.tar.lz
nixlib-d957b4bd78335b845eb5d405be40fa36951b5bea.tar.xz
nixlib-d957b4bd78335b845eb5d405be40fa36951b5bea.tar.zst
nixlib-d957b4bd78335b845eb5d405be40fa36951b5bea.zip
Merge recent master into staging
Hydra nixpkgs: ?compare=1151601
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/default.nix4
-rw-r--r--nixos/lib/test-driver/log2html.xsl4
-rw-r--r--nixos/lib/testing.nix2
-rw-r--r--nixos/modules/config/users-groups.nix2
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh8
-rwxr-xr-xnixos/modules/module-list.nix2
-rw-r--r--nixos/modules/programs/ssh.nix9
-rw-r--r--nixos/modules/services/computing/torque/mom.nix63
-rw-r--r--nixos/modules/services/computing/torque/server.nix96
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix29
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/slave.nix6
-rw-r--r--nixos/modules/services/monitoring/statsd.nix32
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix8
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix11
-rw-r--r--nixos/modules/virtualisation/containers.nix6
-rw-r--r--nixos/modules/virtualisation/nixos-container.pl23
-rw-r--r--nixos/tests/containers.nix30
17 files changed, 293 insertions, 42 deletions
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index bd89ad4eb9cc..68248081af6a 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -57,7 +57,7 @@ let
   };
 
   optionsDocBook = runCommand "options-db.xml" {} ''
-    optionsXML=${options'}/doc/share/nixos/options.xml
+    optionsXML=${options'}/share/doc/nixos/options.xml
     if grep /nixpkgs/nixos/modules $optionsXML; then
       echo "The manual appears to depend on the location of Nixpkgs, which is bad"
       echo "since this prevents sharing via the NixOS channel.  This is typically"
@@ -127,7 +127,7 @@ in rec {
 
       mkdir -p $out/nix-support
       echo "nix-build out $out" >> $out/nix-support/hydra-build-products
-      echo "doc manual $dst manual.html" >> $out/nix-support/hydra-build-products
+      echo "doc manual $dst" >> $out/nix-support/hydra-build-products
     ''; # */
 
     meta.description = "The NixOS manual in HTML format";
diff --git a/nixos/lib/test-driver/log2html.xsl b/nixos/lib/test-driver/log2html.xsl
index ce8a9c6de2b2..0485412b4c8e 100644
--- a/nixos/lib/test-driver/log2html.xsl
+++ b/nixos/lib/test-driver/log2html.xsl
@@ -9,8 +9,8 @@
   <xsl:template match="logfile">
     <html>
       <head>
-        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
-        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
+        <script type="text/javascript" src="jquery.min.js"></script>
+        <script type="text/javascript" src="jquery-ui.min.js"></script>
         <script type="text/javascript" src="treebits.js" />
         <link rel="stylesheet" href="logfile.css" type="text/css" />
         <title>Log File</title>
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index 0e23fc5d187d..75e9c3977763 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -53,6 +53,8 @@ rec {
           xsltproc --output $out/log.html ${./test-driver/log2html.xsl} $out/log.xml
           ln -s ${./test-driver/logfile.css} $out/logfile.css
           ln -s ${./test-driver/treebits.js} $out/treebits.js
+          ln -s ${jquery}/js/jquery.min.js $out/
+          ln -s ${jquery-ui}/js/jquery-ui.min.js $out/
 
           touch $out/nix-support/hydra-build-products
           echo "report testlog $out log.html" >> $out/nix-support/hydra-build-products
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 619f329d74c5..7d0498c10cc5 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -301,7 +301,7 @@ let
   uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.extraUsers) "uid";
   gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.extraGroups) "gid";
 
-  spec = builtins.toFile "users-groups.json" (builtins.toJSON {
+  spec = pkgs.writeText "users-groups.json" (builtins.toJSON {
     inherit (cfg) mutableUsers;
     users = mapAttrsToList (n: u:
       { inherit (u)
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 52b64c37578e..5daa9ff9457c 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -194,13 +194,13 @@ if [ -z "$rollback" ]; then
         nix-env "${extraBuildFlags[@]}" -p "$profile" -f '<nixpkgs/nixos>' --set -A system
         pathToConfig="$profile"
     elif [ "$action" = test -o "$action" = build -o "$action" = dry-run ]; then
-        nix-build '<nixpkgs/nixos>' -A system -K -k "${extraBuildFlags[@]}" > /dev/null
+        nix-build '<nixpkgs/nixos>' -A system -k "${extraBuildFlags[@]}" > /dev/null
         pathToConfig=./result
     elif [ "$action" = build-vm ]; then
-        nix-build '<nixpkgs/nixos>' -A vm -K -k "${extraBuildFlags[@]}" > /dev/null
+        nix-build '<nixpkgs/nixos>' -A vm -k "${extraBuildFlags[@]}" > /dev/null
         pathToConfig=./result
     elif [ "$action" = build-vm-with-bootloader ]; then
-        nix-build '<nixpkgs/nixos>' -A vmWithBootLoader -K -k "${extraBuildFlags[@]}" > /dev/null
+        nix-build '<nixpkgs/nixos>' -A vmWithBootLoader -k "${extraBuildFlags[@]}" > /dev/null
         pathToConfig=./result
     else
         showSyntax
@@ -226,7 +226,7 @@ fi
 # default and/or activate it now.
 if [ "$action" = switch -o "$action" = boot -o "$action" = test ]; then
     if ! $pathToConfig/bin/switch-to-configuration "$action"; then
-        echo "warning: there were error switching to the new configuration" >&2
+        echo "warning: error(s) occured while switching to the new configuration" >&2
         exit 1
     fi
 fi
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 61a98ca12ffb..e40d08c8ecfe 100755
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -95,6 +95,8 @@
   ./services/backup/rsnapshot.nix
   ./services/backup/sitecopy-backup.nix
   ./services/backup/tarsnap.nix
+  ./services/computing/torque/server.nix
+  ./services/computing/torque/mom.nix
   ./services/continuous-integration/jenkins/default.nix
   ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index fdb9dfd4b8c2..ee9cb81a027f 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -59,6 +59,13 @@ in
         '';
       };
 
+      package = mkOption {
+        default = pkgs.openssh;
+        description = ''
+          The package used for the openssh client and daemon.
+        '';
+      };
+
     };
 
   };
@@ -92,7 +99,7 @@ in
         wantedBy = [ "default.target" ];
         serviceConfig =
           { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
-            ExecStart = "${pkgs.openssh}/bin/ssh-agent -a %t/ssh-agent";
+            ExecStart = "${cfg.package}/bin/ssh-agent -a %t/ssh-agent";
             StandardOutput = "null";
             Type = "forking";
             Restart = "on-failure";
diff --git a/nixos/modules/services/computing/torque/mom.nix b/nixos/modules/services/computing/torque/mom.nix
new file mode 100644
index 000000000000..83772539a7ab
--- /dev/null
+++ b/nixos/modules/services/computing/torque/mom.nix
@@ -0,0 +1,63 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.torque.mom;
+  torque = pkgs.torque;
+
+  momConfig = pkgs.writeText "torque-mom-config" ''
+    $pbsserver ${cfg.serverNode}
+    $logevent 225
+  '';
+
+in
+{
+  options = {
+
+    services.torque.mom = {
+      enable = mkEnableOption "torque computing node";
+
+      serverNode = mkOption {
+        type = types.str;
+        description = "Hostname running pbs server.";
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.torque ];
+
+    systemd.services.torque-mom-init = {
+      path = with pkgs; [ torque utillinux procps inetutils ];
+
+      script = ''
+        pbs_mkdirs -v aux
+        pbs_mkdirs -v mom
+        hostname > /var/spool/torque/server_name
+        cp -v ${momConfig} /var/spool/torque/mom_priv/config
+      '';
+
+      serviceConfig.Type = "oneshot";
+      unitConfig.ConditionPathExists = "!/var/spool/torque";
+    };
+
+    systemd.services.torque-mom = {
+      path = [ torque ];
+
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "torque-mom-init.service" ];
+      after = [ "torque-mom-init.service" "network.target" ];
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${torque}/bin/pbs_mom";
+        PIDFile = "/var/spool/torque/mom_priv/mom.lock";
+      };
+    };
+
+  };
+}      
diff --git a/nixos/modules/services/computing/torque/server.nix b/nixos/modules/services/computing/torque/server.nix
new file mode 100644
index 000000000000..655d1500497e
--- /dev/null
+++ b/nixos/modules/services/computing/torque/server.nix
@@ -0,0 +1,96 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.torque.server;
+  torque = pkgs.torque;
+in
+{
+  options = {
+
+    services.torque.server = {
+
+      enable = mkEnableOption "torque server";
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.torque ];
+
+    systemd.services.torque-server-init = {
+      path = with pkgs; [ torque utillinux procps inetutils ];
+
+      script = ''
+        tmpsetup=$(mktemp -t torque-XXXX)
+        cp -p ${torque}/bin/torque.setup $tmpsetup
+        sed -i $tmpsetup -e 's/pbs_server -t create/pbs_server -f -t create/'
+
+        pbs_mkdirs -v aux
+        pbs_mkdirs -v server
+        hostname > /var/spool/torque/server_name
+        cp -prv ${torque}/var/spool/torque/* /var/spool/torque/
+        $tmpsetup root
+
+        sleep 1
+        rm -f $tmpsetup
+        kill $(pgrep pbs_server) 2>/dev/null
+        kill $(pgrep trqauthd) 2>/dev/null
+      '';
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+
+      unitConfig = {
+        ConditionPathExists = "!/var/spool/torque";
+      };
+    };
+
+    systemd.services.trqauthd = {
+      path = [ torque ];
+
+      requires = [ "torque-server-init.service" ];
+      after = [ "torque-server-init.service" ];
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${torque}/bin/trqauthd";
+      };
+    };
+
+    systemd.services.torque-server = {
+      path = [ torque ];
+
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "torque-scheduler.service" "trqauthd.service" ];
+      before = [ "trqauthd.service" ];
+      requires = [ "torque-server-init.service" ];
+      after = [ "torque-server-init.service" "network.target" ];
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${torque}/bin/pbs_server";
+        ExecStop = "${torque}/bin/qterm";
+        PIDFile = "/var/spool/torque/server_priv/server.lock";
+      };
+    };
+
+    systemd.services.torque-scheduler = {
+      path = [ torque ];
+
+      requires = [ "torque-server-init.service" ];
+      after = [ "torque-server-init.service" ];
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${torque}/bin/pbs_sched";
+        PIDFile = "/var/spool/torque/sched_priv/sched.lock";
+      };
+    };
+
+  };
+}      
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index b01b5c3245a4..29a81f066ab9 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -15,7 +15,7 @@ in {
 
       user = mkOption {
         default = "jenkins";
-        type = with types; string;
+        type = types.str;
         description = ''
           User the jenkins server should execute under.
         '';
@@ -23,16 +23,25 @@ in {
 
       group = mkOption {
         default = "jenkins";
-        type = with types; string;
+        type = types.str;
         description = ''
           If the default user "jenkins" is configured then this is the primary
           group of that user.
         '';
       };
 
+      extraGroups = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "wheel" "dialout" ];
+        description = ''
+          List of extra groups that the "jenkins" user should be a part of.
+        '';
+      };
+
       home = mkOption {
         default = "/var/lib/jenkins";
-        type = with types; string;
+        type = types.path;
         description = ''
           The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
           this is the home of the "jenkins" user.
@@ -57,12 +66,21 @@ in {
 
       environment = mkOption {
         default = { NIX_REMOTE = "daemon"; };
-        type = with types; attrsOf string;
+        type = with types; attrsOf str;
         description = ''
           Additional environment variables to be passed to the jenkins process.
           The environment will always include JENKINS_HOME.
         '';
       };
+
+      extraOptions = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "--debug=9" "--httpListenAddress=localhost" ];
+        description = ''
+          Additional command line arguments to pass to Jenkins.
+        '';
+      };
     };
   };
 
@@ -78,6 +96,7 @@ in {
       createHome = true;
       home = cfg.home;
       group = cfg.group;
+      extraGroups = cfg.extraGroups;
       useDefaultShell = true;
       uid = config.ids.uids.jenkins;
     };
@@ -94,7 +113,7 @@ in {
       path = cfg.packages;
 
       script = ''
-        ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins} --httpPort=${toString cfg.port}
+        ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins} --httpPort=${toString cfg.port} ${concatStringsSep " " cfg.extraOptions}
       '';
 
       postStart = ''
diff --git a/nixos/modules/services/continuous-integration/jenkins/slave.nix b/nixos/modules/services/continuous-integration/jenkins/slave.nix
index 5836d92a4fc0..a0216caf2b5c 100644
--- a/nixos/modules/services/continuous-integration/jenkins/slave.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/slave.nix
@@ -23,7 +23,7 @@ in {
 
       user = mkOption {
         default = "jenkins";
-        type = with types; string;
+        type = types.str;
         description = ''
           User the jenkins slave agent should execute under.
         '';
@@ -31,7 +31,7 @@ in {
 
       group = mkOption {
         default = "jenkins";
-        type = with types; string;
+        type = types.str;
         description = ''
           If the default slave agent user "jenkins" is configured then this is
           the primary group of that user.
@@ -40,7 +40,7 @@ in {
 
       home = mkOption {
         default = "/var/lib/jenkins";
-        type = with types; string;
+        type = types.path;
         description = ''
           The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
           this is the home of the "jenkins" user.
diff --git a/nixos/modules/services/monitoring/statsd.nix b/nixos/modules/services/monitoring/statsd.nix
index 74f3deb4c290..942ce72f6a36 100644
--- a/nixos/modules/services/monitoring/statsd.nix
+++ b/nixos/modules/services/monitoring/statsd.nix
@@ -8,13 +8,20 @@ let
 
   configFile = pkgs.writeText "statsd.conf" ''
     {
-      host: "${cfg.host}",
+      address: "${cfg.host}",
       port: "${toString cfg.port}",
       mgmt_address: "${cfg.mgmt_address}",
       mgmt_port: "${toString cfg.mgmt_port}",
-      backends: [${concatMapStrings (el: ''"./backends/${el}",'') cfg.backends}],
-      graphiteHost: "${cfg.graphiteHost}",
-      graphitePort: "${toString cfg.graphitePort}",
+      backends: [${concatMapStringsSep "," (el: if (nixType el) == "string" then ''"./backends/${el}"'' else ''"${head el.names}"'') cfg.backends}],
+      ${optionalString (cfg.graphiteHost!=null) ''graphiteHost: "${cfg.graphiteHost}",''}
+      ${optionalString (cfg.graphitePort!=null) ''graphitePort: "${toString cfg.graphitePort}",''}
+      console: {
+        prettyprint: false
+      },
+      log: {
+        backend: "syslog"
+      },
+      automaticConfigReload: false${optionalString (cfg.extraConfig != null) ","}
       ${cfg.extraConfig}
     }
   '';
@@ -60,24 +67,26 @@ in
     backends = mkOption {
       description = "List of backends statsd will use for data persistance";
       default = ["graphite"];
+      example = ["graphite" pkgs.nodePackages."statsd-influxdb-backend"];
+      type = types.listOf (types.either types.str types.package);
     };
 
     graphiteHost = mkOption {
       description = "Hostname or IP of Graphite server";
-      default = config.services.graphite.web.host;
-      type = types.str;
+      default = null;
+      type = types.nullOr types.str;
     };
 
     graphitePort = mkOption {
       description = "Port of Graphite server (i.e. carbon-cache).";
-      default = 2003;
-      type = types.uniq types.int;
+      default = null;
+      type = types.nullOr types.int;
     };
 
     extraConfig = mkOption {
-      default = "";
       description = "Extra configuration options for statsd";
-      type = types.str;
+      default = "";
+      type = types.nullOr types.str;
     };
 
   };
@@ -95,6 +104,9 @@ in
     systemd.services.statsd = {
       description = "Statsd Server";
       wantedBy = [ "multi-user.target" ];
+      environment = {
+        NODE_PATH=concatMapStringsSep ":" (el: "${el}/lib/node_modules") (filter (el: (nixType el) != "string") cfg.backends);
+      };
       serviceConfig = {
         ExecStart = "${pkgs.nodePackages.statsd}/bin/statsd ${configFile}";
         User = "statsd";
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 379dec2e92c1..fee1bace0460 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -291,7 +291,7 @@ in
       };
 
     environment.etc = authKeysFiles ++ [
-      { source = "${pkgs.openssh}/etc/ssh/moduli";
+      { source = "${cfgc.package}/etc/ssh/moduli";
         target = "ssh/moduli";
       }
       { source = knownHostsFile;
@@ -308,7 +308,7 @@ in
 
             stopIfChanged = false;
 
-            path = [ pkgs.openssh pkgs.gawk ];
+            path = [ cfgc.package pkgs.gawk ];
 
             environment.LD_LIBRARY_PATH = nssModulesPath;
 
@@ -325,7 +325,7 @@ in
 
             serviceConfig =
               { ExecStart =
-                  "${pkgs.openssh}/sbin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
+                  "${cfgc.package}/sbin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
                   "-f ${pkgs.writeText "sshd_config" cfg.extraConfig}";
                 KillMode = "process";
               } // (if cfg.startWhenNeeded then {
@@ -394,7 +394,7 @@ in
         ''}
 
         ${optionalString cfg.allowSFTP ''
-          Subsystem sftp ${pkgs.openssh}/libexec/sftp-server
+          Subsystem sftp ${cfgc.package}/libexec/sftp-server
         ''}
 
         PermitRootLogin ${cfg.permitRootLogin}
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index 2af249a8e961..99460a48835d 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -5,7 +5,7 @@ with lib;
 let
 
   cfg = config.services.tomcat;
-  tomcat = pkgs.tomcat7;
+  tomcat = cfg.package;
 in
 
 {
@@ -21,6 +21,15 @@ in
         description = "Whether to enable Apache Tomcat";
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.tomcat7;
+        example = lib.literalExample "pkgs.tomcat8";
+        description = ''
+          Which tomcat package to use.
+        '';
+      };
+
       baseDir = mkOption {
         default = "/var/tomcat";
         description = "Location where Tomcat stores configuration files, webapplications and logfiles";
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 69b09d7fb314..3a603e0bbac3 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -245,12 +245,6 @@ in
                 ip route add $LOCAL_ADDRESS dev $ifaceHost
               fi
             fi
-
-            # This blocks until the container-startup-done service
-            # writes something to this pipe.  FIXME: it also hangs
-            # until the start timeout expires if systemd-nspawn exits.
-            read x < $root/var/lib/startup-done
-            rm -f $root/var/lib/startup-done
           '';
 
         preStop =
diff --git a/nixos/modules/virtualisation/nixos-container.pl b/nixos/modules/virtualisation/nixos-container.pl
index 7403a42f0f14..9ae5331786cc 100644
--- a/nixos/modules/virtualisation/nixos-container.pl
+++ b/nixos/modules/virtualisation/nixos-container.pl
@@ -201,15 +201,32 @@ sub runInContainer {
     die "cannot run ‘nsenter’: $!\n";
 }
 
+# Remove a directory while recursively unmounting all mounted filesystems within
+# that directory and unmounting/removing that directory afterwards as well.
+#
+# NOTE: If the specified path is a mountpoint, its contents will be removed,
+#       only mountpoints underneath that path will be unmounted properly.
+sub safeRemoveTree {
+    my ($path) = @_;
+    system("find", $path, "-mindepth", "1", "-xdev",
+           "(", "-type", "d", "-exec", "mountpoint", "-q", "{}", ";", ")",
+           "-exec", "umount", "-fR", "{}", "+");
+    system("rm", "--one-file-system", "-rf", $path);
+    if (-e $path) {
+        system("umount", "-fR", $path);
+        system("rm", "--one-file-system", "-rf", $path);
+    }
+}
+
 if ($action eq "destroy") {
     die "$0: cannot destroy declarative container (remove it from your configuration.nix instead)\n"
         unless POSIX::access($confFile, &POSIX::W_OK);
 
     stopContainer if isContainerRunning;
 
-    rmtree($profileDir) if -e $profileDir;
-    rmtree($gcRootsDir) if -e $gcRootsDir;
-    rmtree($root) if -e $root;
+    safeRemoveTree($profileDir) if -e $profileDir;
+    safeRemoveTree($gcRootsDir) if -e $gcRootsDir;
+    safeRemoveTree($root) if -e $root;
     unlink($confFile) or die;
 }
 
diff --git a/nixos/tests/containers.nix b/nixos/tests/containers.nix
index 13d98d742075..331324139a1a 100644
--- a/nixos/tests/containers.nix
+++ b/nixos/tests/containers.nix
@@ -56,12 +56,35 @@ import ./make-test.nix {
 
       die if $id1 eq $id2;
 
+      # Put the root of $id2 into a bind mount.
+      $machine->succeed(
+        "mv /var/lib/containers/$id2 /id2-bindmount",
+        "mount --bind /id2-bindmount /var/lib/containers/$id1"
+      );
+
       my $ip1 = $machine->succeed("nixos-container show-ip $id1");
       chomp $ip1;
       my $ip2 = $machine->succeed("nixos-container show-ip $id2");
       chomp $ip2;
       die if $ip1 eq $ip2;
 
+      # Create a directory and a file we can later check if it still exists
+      # after destruction of the container.
+      $machine->succeed(
+        "mkdir /nested-bindmount",
+        "echo important data > /nested-bindmount/dummy",
+      );
+
+      # Create a directory with a dummy file and bind-mount it into both
+      # containers.
+      foreach ($id1, $id2) {
+        my $importantPath = "/var/lib/containers/$_/very/important/data";
+        $machine->succeed(
+          "mkdir -p $importantPath",
+          "mount --bind /nested-bindmount $importantPath"
+        );
+      }
+
       # Start one of them.
       $machine->succeed("nixos-container start $id1");
 
@@ -72,6 +95,13 @@ import ./make-test.nix {
       $machine->succeed("nixos-container destroy $id1");
       $machine->succeed("nixos-container destroy $id2");
 
+      $machine->succeed(
+        # Check whether destruction of any container has killed important data
+        "grep -qF 'important data' /nested-bindmount/dummy",
+        # Ensure that the container path is gone
+        "test ! -e /var/lib/containers/$id1"
+      );
+
       # Destroying a declarative container should fail.
       $machine->fail("nixos-container destroy webserver");
     '';