summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorRobin Gloster <mail@glob.in>2016-07-31 20:51:34 +0000
committerRobin Gloster <mail@glob.in>2016-07-31 20:51:34 +0000
commit63c7b4f9a7844f0bc84d008b810375eb0fba6b2f (patch)
treee351ecd702d87ab1c4fd03246383162ec19d6de2 /nixos
parent43ba8d295f414ab985bd3fc5d5125421bd8bd0ad (diff)
parenta629cd3f02c2ad071e58d5898c5f48fb0336a705 (diff)
downloadnixlib-63c7b4f9a7844f0bc84d008b810375eb0fba6b2f.tar
nixlib-63c7b4f9a7844f0bc84d008b810375eb0fba6b2f.tar.gz
nixlib-63c7b4f9a7844f0bc84d008b810375eb0fba6b2f.tar.bz2
nixlib-63c7b4f9a7844f0bc84d008b810375eb0fba6b2f.tar.lz
nixlib-63c7b4f9a7844f0bc84d008b810375eb0fba6b2f.tar.xz
nixlib-63c7b4f9a7844f0bc84d008b810375eb0fba6b2f.tar.zst
nixlib-63c7b4f9a7844f0bc84d008b810375eb0fba6b2f.zip
Merge remote-tracking branch 'upstream/master' into hardened-stdenv
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/default.nix57
-rw-r--r--nixos/modules/config/pulseaudio.nix91
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/programs/ssmtp.nix13
-rw-r--r--nixos/modules/programs/tmux.nix9
-rw-r--r--nixos/modules/services/audio/mopidy.nix1
-rw-r--r--nixos/modules/services/continuous-integration/gocd-server/default.nix14
-rw-r--r--nixos/modules/services/logging/logstash.nix7
-rw-r--r--nixos/modules/services/mail/rmilter.nix14
-rw-r--r--nixos/modules/services/mail/rspamd.nix23
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix10
-rw-r--r--nixos/modules/services/monitoring/dd-agent.nix30
-rw-r--r--nixos/modules/services/networking/firewall.nix33
-rw-r--r--nixos/modules/services/networking/syncthing.nix5
-rw-r--r--nixos/modules/services/networking/tinc.nix1
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix35
-rw-r--r--nixos/modules/services/web-servers/lighttpd/cgit.nix5
-rw-r--r--nixos/modules/services/web-servers/lighttpd/default.nix6
-rw-r--r--nixos/modules/services/web-servers/phpfpm.nix (renamed from nixos/modules/services/web-servers/phpfpm/default.nix)34
-rw-r--r--nixos/modules/services/web-servers/phpfpm/pool-options.nix35
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde5.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixos/modules/services/x11/window-managers/mwm.nix25
-rw-r--r--nixos/modules/services/x11/xserver.nix5
-rw-r--r--nixos/modules/system/etc/setup-etc.pl37
-rw-r--r--nixos/modules/virtualisation/containers.nix467
-rw-r--r--nixos/release.nix2
-rw-r--r--nixos/tests/containers-bridge.nix2
-rw-r--r--nixos/tests/containers-extra_veth.nix103
-rw-r--r--nixos/tests/containers-imperative.nix2
-rw-r--r--nixos/tests/containers-ipv4.nix2
-rw-r--r--nixos/tests/containers-ipv6.nix2
-rw-r--r--nixos/tests/gnome3_18-gdm.nix41
-rw-r--r--nixos/tests/gnome3_18.nix38
35 files changed, 738 insertions, 420 deletions
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 50b1bb9c7354..83dad5fd8dc7 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -1,27 +1,27 @@
 { pkgs, options, version, revision, extraSources ? [] }:
 
 with pkgs;
-with pkgs.lib;
 
 let
+  lib = pkgs.lib;
 
   # Remove invisible and internal options.
-  optionsList = filter (opt: opt.visible && !opt.internal) (optionAttrSetToDocList options);
+  optionsList = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
 
   # Replace functions by the string <function>
   substFunction = x:
-    if builtins.isAttrs x then mapAttrs (name: substFunction) x
+    if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x
     else if builtins.isList x then map substFunction x
     else if builtins.isFunction x then "<function>"
     else x;
 
   # Clean up declaration sites to not refer to the NixOS source tree.
-  optionsList' = flip map optionsList (opt: opt // {
+  optionsList' = lib.flip map optionsList (opt: opt // {
     declarations = map stripAnyPrefixes opt.declarations;
   }
-  // optionalAttrs (opt ? example) { example = substFunction opt.example; }
-  // optionalAttrs (opt ? default) { default = substFunction opt.default; }
-  // optionalAttrs (opt ? type) { type = substFunction opt.type; });
+  // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; }
+  // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; }
+  // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; });
 
   # We need to strip references to /nix/store/* from options,
   # including any `extraSources` if some modules came from elsewhere,
@@ -30,7 +30,7 @@ let
   # E.g. if some `options` came from modules in ${pkgs.customModules}/nix,
   # you'd need to include `extraSources = [ pkgs.customModules ]`
   prefixesToStrip = map (p: "${toString p}/") ([ ../../.. ] ++ extraSources);
-  stripAnyPrefixes = flip (fold removePrefix) prefixesToStrip;
+  stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip;
 
   # Convert the list of options into an XML file.
   optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList');
@@ -49,7 +49,7 @@ let
       -o $out ${./options-to-docbook.xsl} $optionsXML
   '';
 
-  sources = sourceFilesBySuffices ./. [".xml"];
+  sources = lib.sourceFilesBySuffices ./. [".xml"];
 
   copySources =
     ''
@@ -143,7 +143,7 @@ in rec {
       mkdir -p $dst
 
       cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON
-        (listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList'))))
+        (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList'))))
       } $dst/options.json
 
       mkdir -p $out/nix-support
@@ -193,6 +193,43 @@ in rec {
     allowedReferences = ["out"];
   };
 
+
+  manualEpub = stdenv.mkDerivation {
+    name = "nixos-manual-epub";
+
+    inherit sources;
+
+    buildInputs = [ libxml2 libxslt zip ];
+
+    buildCommand = ''
+      ${copySources}
+
+      # Check the validity of the manual sources.
+      xmllint --noout --nonet --xinclude --noxincludenode \
+        --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \
+        manual.xml
+
+      # Generate the epub manual.
+      dst=$out/share/doc/nixos
+
+      xsltproc \
+        ${manualXsltprocOptions} \
+        --stringparam target.database.document "${olinkDB}/olinkdb.xml" \
+        --nonet --xinclude --output $dst/epub/ \
+        ${docbook5_xsl}/xml/xsl/docbook/epub/docbook.xsl ./manual.xml
+
+      mkdir -p $dst/epub/OEBPS/images/callouts
+      cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/epub/OEBPS/images/callouts
+      echo "application/epub+zip" > mimetype
+      zip -0Xq  "$dst/NixOS Manual - NixOS community.epub" mimetype
+      zip -Xr9D "$dst/NixOS Manual - NixOS community.epub" $dst/epub/*
+
+      mkdir -p $out/nix-support
+      echo "doc-epub manual $dst/NixOS Manual - NixOS community.epub" >> $out/nix-support/hydra-build-products
+    '';
+  };
+
+
   manualPDF = stdenv.mkDerivation {
     name = "nixos-manual-pdf";
 
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index 7acf050a9a40..71ac3f9a72c4 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -9,11 +9,36 @@ let
 
   systemWide = cfg.enable && cfg.systemWide;
   nonSystemWide = cfg.enable && !cfg.systemWide;
+  hasZeroconf = let z = cfg.zeroconf; in z.publish.enable || z.discovery.enable;
+
+  overriddenPackage = cfg.package.override
+    (optionalAttrs hasZeroconf { zeroconfSupport = true; });
+  binary = "${getBin overriddenPackage}/bin/pulseaudio";
+  binaryNoDaemon = "${binary} --daemonize=no";
 
   # Forces 32bit pulseaudio and alsaPlugins to be built/supported for apps
   # using 32bit alsa on 64bit linux.
   enable32BitAlsaPlugins = cfg.support32Bit && stdenv.isx86_64 && (pkgs_i686.alsaLib != null && pkgs_i686.libpulseaudio != null);
 
+
+  myConfigFile =
+    let
+      addModuleIf = cond: mod: optionalString cond "load-module ${mod}";
+      allAnon = optional cfg.tcp.anonymousClients.allowAll "auth-anonymous=1";
+      ipAnon =  let a = cfg.tcp.anonymousClients.allowedIpRanges;
+                in optional (a != []) ''auth-ip-acl=${concatStringsSep ";" a}'';
+    in writeTextFile {
+      name = "default.pa";
+        text = ''
+        .include ${cfg.configFile}
+        ${addModuleIf cfg.zeroconf.publish.enable "module-zeroconf-publish"}
+        ${addModuleIf cfg.zeroconf.discovery.enable "module-zeroconf-discover"}
+        ${addModuleIf cfg.tcp.enable (concatStringsSep " "
+           ([ "load-module module-native-protocol-tcp" ] ++ allAnon ++ ipAnon))}
+        ${cfg.extraConfig}
+      '';
+    };
+
   ids = config.ids;
 
   uid = ids.uids.pulseaudio;
@@ -26,7 +51,7 @@ let
   # are built with PulseAudio support (like KDE).
   clientConf = writeText "client.conf" ''
     autospawn=${if nonSystemWide then "yes" else "no"}
-    ${optionalString nonSystemWide "daemon-binary=${cfg.package.out}/bin/pulseaudio"}
+    ${optionalString nonSystemWide "daemon-binary=${binary}"}
     ${cfg.extraClientConf}
   '';
 
@@ -44,7 +69,7 @@ let
       hint.description "Default Audio Device (via PulseAudio)"
     }
     ctl_type.pulse {
-      libs.native = ${alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;
+      libs.native = ${pkgs.alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;
       ${lib.optionalString enable32BitAlsaPlugins
      "libs.32Bit = ${pkgs_i686.alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;"}
     }
@@ -89,16 +114,25 @@ in {
       };
 
       configFile = mkOption {
-        type = types.path;
+        type = types.nullOr types.path;
         description = ''
-          The path to the configuration the PulseAudio server
+          The path to the default configuration options the PulseAudio server
           should use. By default, the "default.pa" configuration
           from the PulseAudio distribution is used.
         '';
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Literal string to append to <literal>configFile</literal>
+          and the config file generated by the pulseaudio module.
+        '';
+      };
+
       extraClientConf = mkOption {
-        type = types.str;
+        type = types.lines;
         default = "";
         description = ''
           Extra configuration appended to pulse/client.conf file.
@@ -127,6 +161,31 @@ in {
           '';
         };
       };
+
+      zeroconf = {
+        discovery.enable =
+          mkEnableOption "discovery of pulseaudio sinks in the local network";
+        publish.enable =
+          mkEnableOption "publishing the pulseaudio sink in the local network";
+      };
+
+      # TODO: enable by default?
+      tcp = {
+        enable = mkEnableOption "tcp streaming support";
+
+        anonymousClients = {
+          allowAll = mkEnableOption "all anonymous clients to stream to the server";
+          allowedIpRanges = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = literalExample ''[ "127.0.0.1" "192.168.1.0/24" ]'';
+            description = ''
+              A list of IP subnets that are allowed to stream to the server.
+            '';
+          };
+        };
+      };
+
     };
 
   };
@@ -139,11 +198,11 @@ in {
         source = clientConf;
       };
 
-      hardware.pulseaudio.configFile = mkDefault "${getBin cfg.package}/etc/pulse/default.pa";
+      hardware.pulseaudio.configFile = mkDefault "${getBin overriddenPackage}/etc/pulse/default.pa";
     }
 
     (mkIf cfg.enable {
-      environment.systemPackages = [ cfg.package ];
+      environment.systemPackages = [ overriddenPackage ];
 
       environment.etc = singleton {
         target = "asound.conf";
@@ -152,12 +211,21 @@ in {
 
       # Allow PulseAudio to get realtime priority using rtkit.
       security.rtkit.enable = true;
+
+    })
+
+    (mkIf hasZeroconf {
+      services.avahi.enable = true;
+    })
+    (mkIf cfg.zeroconf.publish.enable {
+      services.avahi.publish.enable = true;
+      services.avahi.publish.userServices = true;
     })
 
     (mkIf nonSystemWide {
       environment.etc = singleton {
         target = "pulse/default.pa";
-        source = cfg.configFile;
+        source = myConfigFile;
       };
 
       systemd.user = {
@@ -167,10 +235,12 @@ in {
           wantedBy = [ "default.target" ];
           serviceConfig = {
             Type = "notify";
-            ExecStart = "${getBin cfg.package}/bin/pulseaudio --daemonize=no";
+            ExecStart = binaryNoDaemon;
             Restart = "on-failure";
+            RestartSec = "500ms";
           };
           environment = { DISPLAY = ":${toString config.services.xserver.display}"; };
+          restartIfChanged = true;
         };
 
         sockets.pulseaudio = {
@@ -205,8 +275,9 @@ in {
         environment.PULSE_RUNTIME_PATH = stateDir;
         serviceConfig = {
           Type = "notify";
-          ExecStart = "${getBin cfg.package}/bin/pulseaudio --daemonize=no --log-level=${cfg.daemon.logLevel} --system -n --file=${cfg.configFile}";
+          ExecStart = "${binaryNoDaemon} --log-level=${cfg.daemon.logLevel} --system -n --file=${myConfigFile}";
           Restart = "on-failure";
+          RestartSec = "500ms";
         };
       };
     })
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 6c696987f89e..9f1ca0f69232 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -473,7 +473,7 @@
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/lighttpd/inginious.nix
   ./services/web-servers/nginx/default.nix
-  ./services/web-servers/phpfpm/default.nix
+  ./services/web-servers/phpfpm.nix
   ./services/web-servers/shellinabox.nix
   ./services/web-servers/tomcat.nix
   ./services/web-servers/uwsgi.nix
diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix
index 7b00efbb4686..f5d1873cc5c8 100644
--- a/nixos/modules/programs/ssmtp.nix
+++ b/nixos/modules/programs/ssmtp.nix
@@ -100,6 +100,12 @@ in
           Password used for SMTP auth. (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)
         '';
       };
+      
+      setSendmail = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Whether to set the system sendmail to ssmtp's.";
+      };
 
     };
 
@@ -122,6 +128,13 @@ in
       '';
 
     environment.systemPackages = [pkgs.ssmtp];
+    
+    services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail {
+      program = "sendmail";
+      source = "${pkgs.ssmtp}/bin/sendmail";
+      setuid = false;
+      setgid = false;
+    };
 
   };
 
diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix
index cadf8d4ae105..f0f8d03505ac 100644
--- a/nixos/modules/programs/tmux.nix
+++ b/nixos/modules/programs/tmux.nix
@@ -27,7 +27,7 @@ let
     set -g status-keys ${cfg.keyMode}
     set -g mode-keys   ${cfg.keyMode}
 
-    ${if cfg.keyMode == "vi" then ''
+    ${if cfg.keyMode == "vi" && cfg.customPaneNavigationAndResize then ''
     bind h select-pane -L
     bind j select-pane -D
     bind k select-pane -U
@@ -86,6 +86,13 @@ in {
         description = "Use 24 hour clock.";
       };
 
+      customPaneNavigationAndResize = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = "Override the hjkl and HJKL bindings for pane navigation and resizing in VI mode.";
+      };
+
       escapeTime = mkOption {
         default = 500;
         example = 0;
diff --git a/nixos/modules/services/audio/mopidy.nix b/nixos/modules/services/audio/mopidy.nix
index 9981b065f28d..029b14ab4726 100644
--- a/nixos/modules/services/audio/mopidy.nix
+++ b/nixos/modules/services/audio/mopidy.nix
@@ -47,6 +47,7 @@ in {
       };
 
       configuration = mkOption {
+        default = "";
         type = types.lines;
         description = ''
           The configuration that Mopidy should use.
diff --git a/nixos/modules/services/continuous-integration/gocd-server/default.nix b/nixos/modules/services/continuous-integration/gocd-server/default.nix
index f0d9d6f53c83..2d1986301216 100644
--- a/nixos/modules/services/continuous-integration/gocd-server/default.nix
+++ b/nixos/modules/services/continuous-integration/gocd-server/default.nix
@@ -67,33 +67,33 @@ in {
       };
 
       packages = mkOption {
-        default = [ pkgs.stdenv pkgs.jre config.programs.ssh.package pkgs.nix ];
+        default = [ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ];
         type = types.listOf types.package;
         description = ''
           Packages to add to PATH for the Go.CD server's process.
         '';
       };
 
-      heapSize = mkOption {
+      initialJavaHeapSize = mkOption {
         default = "512m";
         type = types.str;
         description = ''
-          Specifies the java heap memory size for the Go.CD server's java process.
+          Specifies the initial java heap memory size for the Go.CD server's java process.
         '';
       };
 
-      maxMemory = mkOption {
+      maxJavaHeapMemory = mkOption {
         default = "1024m";
         type = types.str;
         description = ''
-          Specifies the java maximum memory size for the Go.CD server's java process.
+          Specifies the java maximum heap memory size for the Go.CD server's java process.
         '';
       };
 
       extraOptions = mkOption {
         default = [
-          "-Xms${cfg.heapSize}"
-          "-Xmx${cfg.maxMemory}"
+          "-Xms${cfg.initialJavaHeapSize}"
+          "-Xmx${cfg.maxJavaHeapMemory}"
           "-Dcruise.listen.host=${cfg.listenAddress}"
           "-Duser.language=en"
           "-Djruby.rack.request.size.threshold.bytes=30000000"
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index 2c3016d27df8..62f6e187ea07 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -51,12 +51,6 @@ in
         description = "Logging verbosity level.";
       };
 
-      watchdogTimeout = mkOption {
-        type = types.int;
-        default = 10;
-        description = "Set watchdog timeout value in seconds.";
-      };
-
       filterWorkers = mkOption {
         type = types.int;
         default = 1;
@@ -140,7 +134,6 @@ in
           "-w ${toString cfg.filterWorkers} " +
           ops havePluginPath "--pluginpath ${pluginPath} " +
           "${verbosityFlag} " +
-          "--watchdog-timeout ${toString cfg.watchdogTimeout} " +
           "-f ${writeText "logstash.conf" ''
             input {
               ${cfg.inputConfig}
diff --git a/nixos/modules/services/mail/rmilter.nix b/nixos/modules/services/mail/rmilter.nix
index d1f7cd2e173e..e27b38bc0e2e 100644
--- a/nixos/modules/services/mail/rmilter.nix
+++ b/nixos/modules/services/mail/rmilter.nix
@@ -75,7 +75,7 @@ in
 
       bindUnixSockets =  mkOption {
         type = types.listOf types.str;
-        default = ["/run/rmilter.sock"];
+        default = ["/run/rmilter/rmilter.sock"];
         description = ''
           Unix domain sockets to listen for MTA requests.
         '';
@@ -114,7 +114,7 @@ in
 
         servers = mkOption {
           type = types.listOf types.str;
-          default = ["r:/run/rspamd.sock"];
+          default = ["r:/run/rspamd/rspamd.sock"];
           description = ''
             Spamd socket definitions.
             Is server name is prefixed with r: it is rspamd server.
@@ -197,7 +197,7 @@ milter_default_action = accept
 
       serviceConfig = {
         ExecStart = "${pkgs.rmilter}/bin/rmilter ${optionalString cfg.debug "-d"} -n -c ${rmilterConfigFile}";
-        ExecReload = "/bin/kill -USR1 $MAINPID";
+        ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
         User = cfg.user;
         Group = cfg.group;
         PermissionsStartOnly = true;
@@ -212,10 +212,10 @@ milter_default_action = accept
       description = "Rmilter service socket";
       wantedBy = [ "sockets.target" ];
       socketConfig = {
-          ListenStream = cfg.bindUnixSockets ++ cfg.bindInetSockets;
-          SocketUser = cfg.user;
-          SocketGroup = cfg.group;
-          SocketMode = "0660";
+        ListenStream = cfg.bindUnixSockets ++ cfg.bindInetSockets;
+        SocketUser = cfg.user;
+        SocketGroup = cfg.group;
+        SocketMode = "0666";
       };
     };
 
diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index 412b99ccc570..98489df78517 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -8,7 +8,7 @@ let
 
   mkBindSockets = socks: concatStringsSep "\n" (map (each: "  bind_socket = \"${each}\"") socks);
 
-  rspamdConf =
+   rspamdConfFile = pkgs.writeText "rspamd.conf"
     ''
       .include "$CONFDIR/common.conf"
 
@@ -18,8 +18,7 @@ let
       }
 
       logging {
-        type = "file";
-        filename = "$LOGDIR/rspamd.log";
+        type = "syslog";
         .include "$CONFDIR/logging.inc"
       }
 
@@ -33,7 +32,6 @@ let
         .include "$CONFDIR/worker-controller.inc"
       }
    '';
-   rspamdConfFile = pkgs.writeText "rspamd.conf" rspamdConf;
 
 in
 
@@ -45,10 +43,7 @@ in
 
     services.rspamd = {
 
-      enable = mkOption {
-        default = false;
-        description = "Whether to run the rspamd daemon.";
-      };
+      enable = mkEnableOption "Whether to run the rspamd daemon.";
 
       debug = mkOption {
         default = false;
@@ -58,7 +53,7 @@ in
       bindSocket = mkOption {
         type = types.listOf types.str;
         default = [
-          "/run/rspamd.sock mode=0666 owner=${cfg.user}"
+          "/run/rspamd/rspamd.sock mode=0666 owner=${cfg.user}"
         ];
         description = ''
           List of sockets to listen, in format acceptable by rspamd
@@ -97,7 +92,6 @@ in
         '';
        };
     };
-
   };
 
 
@@ -128,18 +122,15 @@ in
 
       serviceConfig = {
         ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c ${rspamdConfFile} -f";
-        RuntimeDirectory = "/var/lib/rspamd";
-        PermissionsStartOnly = true;
         Restart = "always";
+        RuntimeDirectory = "rspamd";
+        PrivateTmp = true;
       };
 
       preStart = ''
-        ${pkgs.coreutils}/bin/mkdir -p /var/{lib,log}/rspamd
+        ${pkgs.coreutils}/bin/mkdir -p /var/lib/rspamd
         ${pkgs.coreutils}/bin/chown ${cfg.user}:${cfg.group} /var/lib/rspamd
       '';
-
     };
-
   };
-
 }
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index 1a95e2d9367d..bb8dc640f981 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -522,10 +522,12 @@ in {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
-        mkdir -p /var/lib/matrix-synapse
-        chmod 700 /var/lib/matrix-synapse
-        chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse
-        ${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse/ --generate-keys
+        if ! test -e /var/lib/matrix-synapse; then
+          mkdir -p /var/lib/matrix-synapse
+          chmod 700 /var/lib/matrix-synapse
+          chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse
+          ${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse/ --generate-keys
+        fi
       '';
       serviceConfig = {
         Type = "simple";
diff --git a/nixos/modules/services/monitoring/dd-agent.nix b/nixos/modules/services/monitoring/dd-agent.nix
index bd8d9950f77c..a98b0f2f0f85 100644
--- a/nixos/modules/services/monitoring/dd-agent.nix
+++ b/nixos/modules/services/monitoring/dd-agent.nix
@@ -72,6 +72,7 @@ let
   postgresqlConfig = pkgs.writeText "postgres.yaml" cfg.postgresqlConfig;
   nginxConfig = pkgs.writeText "nginx.yaml" cfg.nginxConfig;
   mongoConfig = pkgs.writeText "mongo.yaml" cfg.mongoConfig;
+  jmxConfig = pkgs.writeText "jmx.yaml" cfg.jmxConfig;
   
   etcfiles =
     [ { source = ddConf;
@@ -94,6 +95,10 @@ let
     (optional (cfg.mongoConfig != null)
       { source = mongoConfig;
         target = "dd-agent/conf.d/mongo.yaml";
+      }) ++
+    (optional (cfg.jmxConfig != null)
+      { source = jmxConfig;
+        target = "dd-agent/conf.d/jmx.yaml";
       });
 
 in {
@@ -141,6 +146,13 @@ in {
       default = null;
       type = types.uniq (types.nullOr types.string);
     };
+
+    jmxConfig = mkOption {
+      description = "JMX integration configuration";
+      default = null;
+      type = types.uniq (types.nullOr types.string);
+    };
+
   };
 
   config = mkIf cfg.enable {
@@ -167,7 +179,7 @@ in {
         Restart = "always";
         RestartSec = 2;
       };
-      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig ];
+      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig jmxConfig ];
     };
 
     systemd.services.dogstatsd = {
@@ -183,7 +195,21 @@ in {
         Restart = "always";
         RestartSec = 2;
       };
-      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig ];
+      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig jmxConfig ];
+    };
+
+    systemd.services.dd-jmxfetch = lib.mkIf (cfg.jmxConfig != null) {
+      description = "Datadog JMX Fetcher";
+      path = [ pkgs."dd-agent" pkgs.python pkgs.sysstat pkgs.procps pkgs.jdk ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.dd-agent}/bin/dd-jmxfetch";
+        User = "datadog";
+        Group = "datadog";
+        Restart = "always";
+        RestartSec = 2;
+      };
+      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig jmxConfig ];
     };
 
     environment.etc = etcfiles;
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 9221fe155777..138153306dd8 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -101,9 +101,22 @@ let
     # Perform a reverse-path test to refuse spoofers
     # For now, we just drop, as the raw table doesn't have a log-refuse yet
     ${optionalString (kernelHasRPFilter && cfg.checkReversePath) ''
-      if ! ip46tables -A PREROUTING -t raw -m rpfilter --invert -j DROP; then
-        echo "<2>failed to initialise rpfilter support" >&2
-      fi
+      # Clean up rpfilter rules
+      ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2> /dev/null || true
+      ip46tables -t raw -F nixos-fw-rpfilter 2> /dev/null || true
+      ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
+
+      ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter -j RETURN
+
+      # Allows this host to act as a DHCPv4 server
+      iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
+
+      ${optionalString cfg.logReversePathDrops ''
+        ip46tables -t raw -A nixos-fw-rpfilter -j LOG --log-level info --log-prefix "rpfilter drop: "
+      ''}
+      ip46tables -t raw -A nixos-fw-rpfilter -j DROP
+
+      ip46tables -t raw -A PREROUTING -j nixos-fw-rpfilter
     ''}
 
     # Accept all traffic on the trusted interfaces.
@@ -188,9 +201,7 @@ let
     ip46tables -D INPUT -j nixos-fw 2>/dev/null || true
 
     ${optionalString (kernelHasRPFilter && cfg.checkReversePath) ''
-      if ! ip46tables -D PREROUTING -t raw -m rpfilter --invert -j DROP; then
-        echo "<2>failed to stop rpfilter support" >&2
-      fi
+      ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2>/dev/null || true
     ''}
 
     ${cfg.extraStopCommands}
@@ -376,6 +387,16 @@ in
         '';
     };
 
+    networking.firewall.logReversePathDrops = mkOption {
+      default = false;
+      type = types.bool;
+      description =
+        ''
+          Logs dropped packets failing the reverse path filter test if
+          the option networking.firewall.checkReversePath is enabled.
+        '';
+    };
+
     networking.firewall.connectionTrackingModules = mkOption {
       default = [ "ftp" ];
       example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index 98f4faabba26..b44b03dc0bf3 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -114,8 +114,8 @@ in
 
     environment.systemPackages = [ cfg.package ];
 
-    systemd.services.syncthing = mkIf cfg.systemService
-      header // {
+    systemd.services = mkIf cfg.systemService {
+      syncthing = header // {
         wantedBy = [ "multi-user.target" ];
         serviceConfig = service // {
           User = cfg.user;
@@ -124,6 +124,7 @@ in
           ExecStart = "${cfg.package}/bin/syncthing -no-browser -home=${cfg.dataDir}";
         };
       };
+    };
 
     systemd.user.services.syncthing =
       header // {
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index 9330e6c92ba8..8da0f817ae2a 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -157,6 +157,7 @@ in
         serviceConfig = {
           Type = "simple";
           PIDFile = "/run/tinc.${network}.pid";
+          Restart = "on-failure";
         };
         preStart = ''
           mkdir -p /etc/tinc/${network}/hosts
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 541aac1d0ef1..b08070f1e366 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -17,6 +17,7 @@ let
     else cfg.database.port;
 
   poolName = "tt-rss";
+  phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
   virtualHostName = "tt-rss";
 
   tt-rss-config = pkgs.writeText "config.php" ''
@@ -448,23 +449,21 @@ let
     root = "/var/lib/tt-rss";
   in mkIf cfg.enable {
 
-    services.phpfpm.pools = if cfg.pool == "${poolName}" then {
-      "${poolName}" =  {
-        listen = "/var/run/phpfpm/${poolName}.sock";
-        extraConfig = ''
-          listen.owner = nginx
-          listen.group = nginx
-          listen.mode = 0600
-          user = nginx
-          pm = dynamic
-          pm.max_children = 75
-          pm.start_servers = 10
-          pm.min_spare_servers = 5
-          pm.max_spare_servers = 20
-          pm.max_requests = 500
-          catch_workers_output = 1
-        '';
-      };
+    services.phpfpm.poolConfigs = if cfg.pool == "${poolName}" then {
+      "${poolName}" = ''
+        listen = "${phpfpmSocketName}";
+        listen.owner = nginx
+        listen.group = nginx
+        listen.mode = 0600
+        user = nginx
+        pm = dynamic
+        pm.max_children = 75
+        pm.start_servers = 10
+        pm.min_spare_servers = 5
+        pm.max_spare_servers = 20
+        pm.max_requests = 500
+        catch_workers_output = 1
+      '';
     } else {};
 
     # TODO: Re-enable after https://github.com/NixOS/nixpkgs/pull/15862 is merged
@@ -486,7 +485,7 @@ let
     #     locations."~ \.php$" = {
     #       extraConfig = ''
     #         fastcgi_split_path_info ^(.+\.php)(/.+)$;
-    #         fastcgi_pass unix:${config.services.phpfpm.pools."${cfg.pool}".listen};
+    #         fastcgi_pass unix:${phpfpmSocketName};
     #         fastcgi_index index.php;
     #         fastcgi_param SCRIPT_FILENAME ${root}/$fastcgi_script_name;
 
diff --git a/nixos/modules/services/web-servers/lighttpd/cgit.nix b/nixos/modules/services/web-servers/lighttpd/cgit.nix
index c8590e6a54e1..710fecc0c05c 100644
--- a/nixos/modules/services/web-servers/lighttpd/cgit.nix
+++ b/nixos/modules/services/web-servers/lighttpd/cgit.nix
@@ -63,6 +63,11 @@ in
       }
     '';
 
+    systemd.services.lighttpd.preStart = ''
+      mkdir -p /var/cache/cgit
+      chown lighttpd:lighttpd /var/cache/cgit
+    '';
+
   };
 
 }
diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix
index 171503db4eec..84cd0a4095d9 100644
--- a/nixos/modules/services/web-servers/lighttpd/default.nix
+++ b/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -224,12 +224,6 @@ in
       description = "Lighttpd Web Server";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        ${if cfg.cgit.enable then ''
-          mkdir -p /var/cache/cgit
-          chown lighttpd:lighttpd /var/cache/cgit
-        '' else ""}
-      '';
       serviceConfig.ExecStart = "${pkgs.lighttpd}/sbin/lighttpd -D -f ${configFile}";
       # SIGINT => graceful shutdown
       serviceConfig.KillSignal = "SIGINT";
diff --git a/nixos/modules/services/web-servers/phpfpm/default.nix b/nixos/modules/services/web-servers/phpfpm.nix
index 6befddf9f522..2658d7117e37 100644
--- a/nixos/modules/services/web-servers/phpfpm/default.nix
+++ b/nixos/modules/services/web-servers/phpfpm.nix
@@ -9,12 +9,6 @@ let
 
   pidFile = "${stateDir}/phpfpm.pid";
 
-  mkPool = n: p: ''
-    [${n}]
-    listen = ${p.listen}
-    ${p.extraConfig}
-  '';
-
   cfgFile = pkgs.writeText "phpfpm.conf" ''
     [global]
     pid = ${pidFile}
@@ -22,7 +16,7 @@ let
     daemonize = yes
     ${cfg.extraConfig}
 
-    ${concatStringsSep "\n" (mapAttrsToList mkPool cfg.pools)}
+    ${concatStringsSep "\n" (mapAttrsToList (n: v: "[${n}]\n${v}") cfg.poolConfigs)}
   '';
 
   phpIni = pkgs.writeText "php.ini" ''
@@ -67,19 +61,33 @@ in {
           "Options appended to the PHP configuration file <filename>php.ini</filename>.";
       };
 
-      pools = mkOption {
-        type = types.attrsOf (types.submodule (import ./pool-options.nix {
-          inherit lib;
-        }));
+      poolConfigs = mkOption {
+        type = types.attrsOf types.lines;
         default = {};
+        example = literalExample ''
+          { mypool = '''
+              listen = /run/phpfpm/mypool
+              user = nobody
+              pm = dynamic
+              pm.max_children = 75
+              pm.start_servers = 10
+              pm.min_spare_servers = 5
+              pm.max_spare_servers = 20
+              pm.max_requests = 500
+            ''';
+          }
+        '';
         description = ''
-          If no pools are defined, the phpfpm service is disabled.
+          A mapping between PHP FPM pool names and their configurations.
+          See the documentation on <literal>php-fpm.conf</literal> for
+          details on configuration directives. If no pools are defined,
+          the phpfpm service is disabled.
         '';
       };
     };
   };
 
-  config = mkIf (cfg.pools != {}) {
+  config = mkIf (cfg.poolConfigs != {}) {
 
     systemd.services.phpfpm = {
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/web-servers/phpfpm/pool-options.nix b/nixos/modules/services/web-servers/phpfpm/pool-options.nix
deleted file mode 100644
index cc688c2c48a2..000000000000
--- a/nixos/modules/services/web-servers/phpfpm/pool-options.nix
+++ /dev/null
@@ -1,35 +0,0 @@
-{ lib }:
-
-with lib; {
-
-  options = {
-
-    listen = mkOption {
-      type = types.str;
-      example = "/path/to/unix/socket";
-      description = ''
-        The address on which to accept FastCGI requests.
-      '';
-    };
-
-    extraConfig = mkOption {
-      type = types.lines;
-      example = ''
-        user = nobody
-        pm = dynamic
-        pm.max_children = 75
-        pm.start_servers = 10
-        pm.min_spare_servers = 5
-        pm.max_spare_servers = 20
-        pm.max_requests = 500
-      '';
-
-      description = ''
-        Extra lines that go into the pool configuration.
-        See the documentation on <literal>php-fpm.conf</literal> for
-        details on configuration directives.
-      '';
-    };
-  };
-}
-
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 44c87c0760cb..b3da25448029 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -84,7 +84,7 @@ in {
 
     environment.gnome3.packageSet = mkOption {
       default = null;
-      example = literalExample "pkgs.gnome3_18";
+      example = literalExample "pkgs.gnome3_20";
       description = "Which GNOME 3 package set to use.";
       apply = p: if p == null then pkgs.gnome3 else p;
     };
diff --git a/nixos/modules/services/x11/desktop-managers/kde5.nix b/nixos/modules/services/x11/desktop-managers/kde5.nix
index 9f9bbe246234..86b98c2b6601 100644
--- a/nixos/modules/services/x11/desktop-managers/kde5.nix
+++ b/nixos/modules/services/x11/desktop-managers/kde5.nix
@@ -148,7 +148,7 @@ in
         kde5.kde-gtk-config
 
         pkgs.phonon-backend-gstreamer
-        pkgs.kde5.phonon-backend-gstreamer
+        pkgs.qt5.phonon-backend-gstreamer
       ]
 
       # Plasma 5.5 and later has a Breeze GTK theme.
@@ -214,7 +214,7 @@ in
     services.xserver.displayManager.sddm = {
       theme = "breeze";
       themes = [
-        kde5.extra-cmake-modules # for the setup-hook
+        kde5.ecm # for the setup-hook
         kde5.plasma-workspace
         kde5.breeze-icons
         (kde5.oxygen-icons or kde5.oxygen-icons5)
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 94f1aa3add7e..f005decfa33c 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -18,6 +18,7 @@ in
     ./i3.nix
     ./jwm.nix
     ./metacity.nix
+    ./mwm.nix
     ./openbox.nix
     ./pekwm.nix
     ./notion.nix
diff --git a/nixos/modules/services/x11/window-managers/mwm.nix b/nixos/modules/services/x11/window-managers/mwm.nix
new file mode 100644
index 000000000000..31f7b725f747
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/mwm.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.windowManager.mwm;
+in
+{
+  ###### interface
+  options = {
+    services.xserver.windowManager.mwm.enable = mkEnableOption "mwm";
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton {
+      name = "mwm";
+      start = ''
+        ${pkgs.motif}/bin/mwm &
+        waitPID=$!
+      '';
+    };
+    environment.systemPackages = [ pkgs.motif ];
+  };
+}
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 35816c3a16dd..e7df2e6b4690 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -14,9 +14,6 @@ let
   # Map video driver names to driver packages. FIXME: move into card-specific modules.
   knownVideoDrivers = {
     virtualbox = { modules = [ kernelPackages.virtualboxGuestAdditions ]; driverName = "vboxvideo"; };
-    ati = { modules = with pkgs.xorg; [ xf86videoati glamoregl ]; };
-    intel = { modules = with pkgs.xorg; [ xf86videointel glamoregl ]; };
-    modesetting = { modules = []; };
   };
 
   fontsForXServer =
@@ -512,7 +509,7 @@ in
             XKB_BINDIR = "${xorg.xkbcomp}/bin"; # Needed for the Xkb extension.
             XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime.
             LD_LIBRARY_PATH = concatStringsSep ":" (
-              [ "${xorg.libX11.out}/lib" "${xorg.libXext.out}/lib" ]
+              [ "${xorg.libX11.out}/lib" "${xorg.libXext.out}/lib" "/run/opengl-driver/lib" ]
               ++ concatLists (catAttrs "libPath" cfg.drivers));
           } // cfg.displayManager.job.environment;
 
diff --git a/nixos/modules/system/etc/setup-etc.pl b/nixos/modules/system/etc/setup-etc.pl
index 89a49b972ff9..efda74161ff8 100644
--- a/nixos/modules/system/etc/setup-etc.pl
+++ b/nixos/modules/system/etc/setup-etc.pl
@@ -22,6 +22,33 @@ sub atomicSymlink {
 # current configuration.
 atomicSymlink $etc, $static or die;
 
+# Returns 1 if the argument points to the files in /etc/static.  That
+# means either argument is a symlink to a file in /etc/static or a
+# directory with all children being static.
+sub isStatic {
+    my $path = shift;
+
+    if (-l $path) {
+        my $target = readlink $path;
+        return substr($target, 0, length "/etc/static/") eq "/etc/static/";
+    }
+
+    if (-d $path) {
+        opendir DIR, "$path" or return 0;
+        my @names = readdir DIR or die;
+        closedir DIR;
+
+        foreach my $name (@names) {
+            next if $name eq "." || $name eq "..";
+            unless (isStatic("$path/$name")) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+
+    return 0;
+}
 
 # Remove dangling symlinks that point to /etc/static.  These are
 # configuration files that existed in a previous configuration but not
@@ -63,6 +90,16 @@ sub link {
     my $target = "/etc/$fn";
     File::Path::make_path(dirname $target);
     $created{$fn} = 1;
+
+    # Rename doesn't work if target is directory.
+    if (-l $_ && -d $target) {
+        if (isStatic $target) {
+            rmtree $target or warn;
+        } else {
+            warn "$target directory contains user files. Symlinking may fail.";
+        }
+    }
+
     if (-e "$_.mode") {
         my $mode = read_file("$_.mode"); chomp $mode;
         if ($mode eq "direct-symlink") {
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index b3870caece0c..d83841452f95 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -6,37 +6,212 @@ let
 
   # The container's init script, a small wrapper around the regular
   # NixOS stage-2 init script.
-  containerInit = pkgs.writeScript "container-init"
-    ''
-      #! ${pkgs.stdenv.shell} -e
+  containerInit = (cfg:
+    let
+      renderExtraVeth = (name: cfg:
+        ''
+        echo "Bringing ${name} up"
+        ip link set dev ${name} up
+        ${optionalString (cfg . "localAddress" or null != null) ''
+          echo "Setting ip for ${name}"
+          ip addr add ${cfg . "localAddress"} dev ${name}
+        ''}
+        ${optionalString (cfg . "localAddress6" or null != null) ''
+          echo "Setting ip6 for ${name}"
+          ip -6 addr add ${cfg . "localAddress6"} dev ${name}
+        ''}
+        ${optionalString (cfg . "hostAddress" or null != null) ''
+          echo "Setting route to host for ${name}"
+          ip route add ${cfg . "hostAddress"} dev ${name}
+        ''}
+        ${optionalString (cfg . "hostAddress6" or null != null) ''
+          echo "Setting route6 to host for ${name}"
+          ip -6 route add ${cfg . "hostAddress6"} dev ${name}
+        ''}
+        ''
+        );
+    in
+      pkgs.writeScript "container-init"
+      ''
+        #! ${pkgs.stdenv.shell} -e
 
-      # Initialise the container side of the veth pair.
-      if [ "$PRIVATE_NETWORK" = 1 ]; then
+        # Initialise the container side of the veth pair.
+        if [ "$PRIVATE_NETWORK" = 1 ]; then
 
-        ip link set host0 name eth0
-        ip link set dev eth0 up
+          ip link set host0 name eth0
+          ip link set dev eth0 up
 
-        if [ -n "$LOCAL_ADDRESS" ]; then
-          ip addr add $LOCAL_ADDRESS dev eth0
-        fi
-        if [ -n "$LOCAL_ADDRESS6" ]; then
-          ip -6 addr add $LOCAL_ADDRESS6 dev eth0
+          if [ -n "$LOCAL_ADDRESS" ]; then
+            ip addr add $LOCAL_ADDRESS dev eth0
+          fi
+          if [ -n "$LOCAL_ADDRESS6" ]; then
+            ip -6 addr add $LOCAL_ADDRESS6 dev eth0
+          fi
+          if [ -n "$HOST_ADDRESS" ]; then
+            ip route add $HOST_ADDRESS dev eth0
+            ip route add default via $HOST_ADDRESS
+          fi
+          if [ -n "$HOST_ADDRESS6" ]; then
+            ip -6 route add $HOST_ADDRESS6 dev eth0
+            ip -6 route add default via $HOST_ADDRESS6
+          fi
+
+          ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg . "extraVeths" or {})}
+          ip a
+          ip r
         fi
-        if [ -n "$HOST_ADDRESS" ]; then
-          ip route add $HOST_ADDRESS dev eth0
-          ip route add default via $HOST_ADDRESS
+
+        # Start the regular stage 1 script.
+        exec "$1"
+      ''
+    );
+
+  nspawnExtraVethArgs = (name: cfg: "--network-veth-extra=${name}");
+  startScript = (cfg:
+    ''
+      mkdir -p -m 0755 "$root/etc" "$root/var/lib"
+      mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
+      if ! [ -e "$root/etc/os-release" ]; then
+        touch "$root/etc/os-release"
+      fi
+
+      if ! [ -e "$root/etc/machine-id" ]; then
+        touch "$root/etc/machine-id"
+      fi
+
+      mkdir -p -m 0755 \
+        "/nix/var/nix/profiles/per-container/$INSTANCE" \
+        "/nix/var/nix/gcroots/per-container/$INSTANCE"
+
+      cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
+
+      if [ "$PRIVATE_NETWORK" = 1 ]; then
+        extraFlags+=" --network-veth"
+        if [ -n "$HOST_BRIDGE" ]; then
+          extraFlags+=" --network-bridge=$HOST_BRIDGE"
         fi
-        if [ -n "$HOST_ADDRESS6" ]; then
-          ip -6 route add $HOST_ADDRESS6 dev eth0
-          ip -6 route add default via $HOST_ADDRESS6
+      fi
+
+      ${if cfg . "extraVeths" or null != null then
+        ''extraFlags+=" ${concatStringsSep " " (mapAttrsToList nspawnExtraVethArgs cfg . "extraVeths" or {})}"''
+        else
+          ''# No extra veth pairs to create''
+      }
+
+      for iface in $INTERFACES; do
+        extraFlags+=" --network-interface=$iface"
+      done
+
+      for iface in $MACVLANS; do
+        extraFlags+=" --network-macvlan=$iface"
+      done
+
+      # If the host is 64-bit and the container is 32-bit, add a
+      # --personality flag.
+      ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
+        if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
+          extraFlags+=" --personality=x86"
         fi
+      ''}
+
+      # Run systemd-nspawn without startup notification (we'll
+      # wait for the container systemd to signal readiness).
+      EXIT_ON_REBOOT=1 \
+      exec ${config.systemd.package}/bin/systemd-nspawn \
+        --keep-unit \
+        -M "$INSTANCE" -D "$root" $extraFlags \
+        $EXTRA_NSPAWN_FLAGS \
+        --notify-ready=yes \
+        --bind-ro=/nix/store \
+        --bind-ro=/nix/var/nix/db \
+        --bind-ro=/nix/var/nix/daemon-socket \
+        --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
+        --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
+        --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
+        --setenv HOST_BRIDGE="$HOST_BRIDGE" \
+        --setenv HOST_ADDRESS="$HOST_ADDRESS" \
+        --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
+        --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
+        --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
+        --setenv PATH="$PATH" \
+        ${containerInit cfg} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
+    ''
+    );
+
+  preStartScript = (cfg:
+    ''
+      # Clean up existing machined registration and interfaces.
+      machinectl terminate "$INSTANCE" 2> /dev/null || true
+
+      if [ "$PRIVATE_NETWORK" = 1 ]; then
+        ip link del dev "ve-$INSTANCE" 2> /dev/null || true
+        ip link del dev "vb-$INSTANCE" 2> /dev/null || true
       fi
 
-      # Start the regular stage 1 script, passing the bind-mounted
-      # notification socket from the host to allow the container
-      # systemd to signal readiness to the host systemd.
-      NOTIFY_SOCKET=/var/lib/private/host-notify exec "$1"
-    '';
+      ${concatStringsSep "\n" (
+        mapAttrsToList (name: cfg:
+          ''ip link del dev ${name} 2> /dev/null || true ''
+        ) cfg . "extraVeths" or {}
+      )}
+   ''
+    );
+  postStartScript = (cfg:
+    let
+      ipcall = (cfg: ipcmd: variable: attribute:
+        if cfg . attribute or null == null then
+          ''
+            if [ -n "${variable}" ]; then
+              ${ipcmd} add ${variable} dev $ifaceHost
+            fi
+          ''
+        else
+          ''${ipcmd} add ${cfg . attribute} dev $ifaceHost''
+        );
+      renderExtraVeth = (name: cfg:
+        if cfg . "hostBridge" or null != null then
+          ''
+            # Add ${name} to bridge ${cfg.hostBridge}
+            ip link set dev ${name} master ${cfg.hostBridge} up
+          ''
+        else
+          ''
+          # Set IPs and routes for ${name}
+          ${optionalString (cfg . "hostAddress" or null != null) ''
+            ip addr add ${cfg . "hostAddress"} dev ${name}
+          ''}
+          ${optionalString (cfg . "hostAddress6" or null != null) ''
+            ip -6 addr add ${cfg . "hostAddress6"} dev ${name}
+          ''}
+          ${optionalString (cfg . "localAddress" or null != null) ''
+            ip route add ${cfg . "localAddress"} dev ${name}
+          ''}
+          ${optionalString (cfg . "localAddress6" or null != null) ''
+            ip -6 route add ${cfg . "localAddress6"} dev ${name}
+          ''}
+          ''
+        );
+    in
+      ''
+        if [ "$PRIVATE_NETWORK" = 1 ]; then
+          if [ -z "$HOST_BRIDGE" ]; then
+            ifaceHost=ve-$INSTANCE
+            ip link set dev $ifaceHost up
+
+            ${ipcall cfg "ip addr" "$HOST_ADDRESS" "hostAddress"}
+            ${ipcall cfg "ip -6 addr" "$HOST_ADDRESS6" "hostAddress6"}
+            ${ipcall cfg "ip route" "$LOCAL_ADDRESS" "localAddress"}
+            ${ipcall cfg "ip -6 route" "$LOCAL_ADDRESS6" "localAddress6"}
+          fi
+          ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg . "extraVeths" or {})}
+        fi
+
+        # Get the leader PID so that we can signal it in
+        # preStop. We can't use machinectl there because D-Bus
+        # might be shutting down. FIXME: in systemd 219 we can
+        # just signal systemd-nspawn to do a clean shutdown.
+        machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
+      ''
+  );
 
   system = config.nixpkgs.system;
 
@@ -75,6 +250,63 @@ let
 
   mkBindFlags = bs: concatMapStrings mkBindFlag (lib.attrValues bs);
 
+  networkOptions = {
+    hostBridge = mkOption {
+      type = types.nullOr types.string;
+      default = null;
+      example = "br0";
+      description = ''
+        Put the host-side of the veth-pair into the named bridge.
+        Only one of hostAddress* or hostBridge can be given.
+      '';
+    };
+
+    hostAddress = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "10.231.136.1";
+      description = ''
+        The IPv4 address assigned to the host interface.
+        (Not used when hostBridge is set.)
+      '';
+    };
+
+    hostAddress6 = mkOption {
+      type = types.nullOr types.string;
+      default = null;
+      example = "fc00::1";
+      description = ''
+        The IPv6 address assigned to the host interface.
+        (Not used when hostBridge is set.)
+      '';
+    };
+
+    localAddress = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "10.231.136.2";
+      description = ''
+        The IPv4 address assigned to the interface in the container.
+        If a hostBridge is used, this should be given with netmask to access
+        the whole network. Otherwise the default netmask is /32 and routing is
+        set up from localAddress to hostAddress and back.
+      '';
+    };
+
+    localAddress6 = mkOption {
+      type = types.nullOr types.string;
+      default = null;
+      example = "fc00::2";
+      description = ''
+        The IPv6 address assigned to the interface in the container.
+        If a hostBridge is used, this should be given with netmask to access
+        the whole network. Otherwise the default netmask is /128 and routing is
+        set up from localAddress6 to hostAddress6 and back.
+      '';
+    };
+
+  };
+
 in
 
 {
@@ -135,56 +367,6 @@ in
               '';
             };
 
-            hostBridge = mkOption {
-              type = types.nullOr types.string;
-              default = null;
-              example = "br0";
-              description = ''
-                Put the host-side of the veth-pair into the named bridge.
-                Only one of hostAddress* or hostBridge can be given.
-              '';
-            };
-
-            hostAddress = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              example = "10.231.136.1";
-              description = ''
-                The IPv4 address assigned to the host interface.
-                (Not used when hostBridge is set.)
-              '';
-            };
-
-            hostAddress6 = mkOption {
-              type = types.nullOr types.string;
-              default = null;
-              example = "fc00::1";
-              description = ''
-                The IPv6 address assigned to the host interface.
-                (Not used when hostBridge is set.)
-              '';
-            };
-
-            localAddress = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              example = "10.231.136.2";
-              description = ''
-                The IPv4 address assigned to <literal>eth0</literal>
-                in the container.
-              '';
-            };
-
-            localAddress6 = mkOption {
-              type = types.nullOr types.string;
-              default = null;
-              example = "fc00::2";
-              description = ''
-                The IPv6 address assigned to <literal>eth0</literal>
-                in the container.
-              '';
-            };
-
             interfaces = mkOption {
               type = types.listOf types.string;
               default = [];
@@ -194,6 +376,15 @@ in
               '';
             };
 
+            extraVeths = mkOption {
+              type = types.attrsOf types.optionSet;
+              default = {};
+              options = networkOptions;
+              description = ''
+                Extra veth-pairs to be created for the container
+              '';
+            };
+
             autoStart = mkOption {
               type = types.bool;
               default = false;
@@ -216,7 +407,7 @@ in
                 '';
             };
 
-          };
+          } // networkOptions;
 
           config = mkMerge
             [ (mkIf options.config.isDefined {
@@ -274,110 +465,11 @@ in
       environment.INSTANCE = "%i";
       environment.root = "/var/lib/containers/%i";
 
-      preStart =
-        ''
-          # Clean up existing machined registration and interfaces.
-          machinectl terminate "$INSTANCE" 2> /dev/null || true
-
-          if [ "$PRIVATE_NETWORK" = 1 ]; then
-            ip link del dev "ve-$INSTANCE" 2> /dev/null || true
-            ip link del dev "vb-$INSTANCE" 2> /dev/null || true
-          fi
-       '';
-
-      script =
-        ''
-          mkdir -p -m 0755 "$root/etc" "$root/var/lib"
-          mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
-          if ! [ -e "$root/etc/os-release" ]; then
-            touch "$root/etc/os-release"
-          fi
-
-          if ! [ -e "$root/etc/machine-id" ]; then
-            touch "$root/etc/machine-id"
-          fi
-
-          mkdir -p -m 0755 \
-            "/nix/var/nix/profiles/per-container/$INSTANCE" \
-            "/nix/var/nix/gcroots/per-container/$INSTANCE"
-
-          cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
-
-          if [ "$PRIVATE_NETWORK" = 1 ]; then
-            extraFlags+=" --network-veth"
-            if [ -n "$HOST_BRIDGE" ]; then
-              extraFlags+=" --network-bridge=$HOST_BRIDGE"
-            fi
-          fi
+      preStart = preStartScript {};
 
-          for iface in $INTERFACES; do
-            extraFlags+=" --network-interface=$iface"
-          done
+      script = startScript {};
 
-          for iface in $MACVLANS; do
-            extraFlags+=" --network-macvlan=$iface"
-          done
-
-          # If the host is 64-bit and the container is 32-bit, add a
-          # --personality flag.
-          ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
-            if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
-              extraFlags+=" --personality=x86"
-            fi
-          ''}
-
-          rm -f $root/var/lib/private/host-notify
-
-          # Run systemd-nspawn without startup notification (we'll
-          # wait for the container systemd to signal readiness).
-          EXIT_ON_REBOOT=1 NOTIFY_SOCKET= \
-          exec ${config.systemd.package}/bin/systemd-nspawn \
-            --keep-unit \
-            -M "$INSTANCE" -D "$root" $extraFlags \
-            $EXTRA_NSPAWN_FLAGS \
-            --bind-ro=/nix/store \
-            --bind-ro=/nix/var/nix/db \
-            --bind-ro=/nix/var/nix/daemon-socket \
-            --bind=/run/systemd/notify:/var/lib/private/host-notify \
-            --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
-            --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
-            --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
-            --setenv HOST_BRIDGE="$HOST_BRIDGE" \
-            --setenv HOST_ADDRESS="$HOST_ADDRESS" \
-            --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
-            --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
-            --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
-            --setenv PATH="$PATH" \
-            ${containerInit} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
-        '';
-
-      postStart =
-        ''
-          if [ "$PRIVATE_NETWORK" = 1 ]; then
-            if [ -z "$HOST_BRIDGE" ]; then
-              ifaceHost=ve-$INSTANCE
-              ip link set dev $ifaceHost up
-              if [ -n "$HOST_ADDRESS" ]; then
-                ip addr add $HOST_ADDRESS dev $ifaceHost
-              fi
-              if [ -n "$HOST_ADDRESS6" ]; then
-                ip -6 addr add $HOST_ADDRESS6 dev $ifaceHost
-              fi
-              if [ -n "$LOCAL_ADDRESS" ]; then
-                ip route add $LOCAL_ADDRESS dev $ifaceHost
-              fi
-              if [ -n "$LOCAL_ADDRESS6" ]; then
-                ip -6 route add $LOCAL_ADDRESS6 dev $ifaceHost
-              fi
-            fi
-          fi
-
-          # Get the leader PID so that we can signal it in
-          # preStop. We can't use machinectl there because D-Bus
-          # might be shutting down. FIXME: in systemd 219 we can
-          # just signal systemd-nspawn to do a clean shutdown.
-          machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
-        '';
+      postStart = postStartScript {};
 
       preStop =
         ''
@@ -404,8 +496,6 @@ in
 
         Type = "notify";
 
-        NotifyAccess = "all";
-
         # Note that on reboot, systemd-nspawn returns 133, so this
         # unit will be restarted. On poweroff, it returns 0, so the
         # unit won't be restarted.
@@ -421,6 +511,8 @@ in
         # after the timeout). So send an ignored signal.
         KillMode = "mixed";
         KillSignal = "WINCH";
+
+        DevicePolicy = "closed";
       };
     };
   in {
@@ -429,15 +521,20 @@ in
       [{ name = "container@"; value = unit; }]
       # declarative containers
       ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (
+        unit // {
+          preStart = preStartScript cfg;
+          script = startScript cfg;
+          postStart = postStartScript cfg;
+        } // (
         if cfg.autoStart then
-          unit // {
+          {
             wantedBy = [ "multi-user.target" ];
             wants = [ "network.target" ];
             after = [ "network.target" ];
             restartTriggers = [ cfg.path ];
             reloadIfChanged = true;
           }
-        else null
+        else {})
       )) config.containers)
     ));
 
@@ -466,11 +563,11 @@ in
                 LOCAL_ADDRESS6=${cfg.localAddress6}
               ''}
             ''}
-             INTERFACES="${toString cfg.interfaces}"
-           ${optionalString cfg.autoStart ''
-             AUTO_START=1
-           ''}
-           EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts}"
+            INTERFACES="${toString cfg.interfaces}"
+            ${optionalString cfg.autoStart ''
+              AUTO_START=1
+            ''}
+            EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts}"
           '';
       }) config.containers;
 
diff --git a/nixos/release.nix b/nixos/release.nix
index 273c3e21a10d..68f06a832980 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -95,6 +95,7 @@ in rec {
   channel = import lib/make-channel.nix { inherit pkgs nixpkgs version versionSuffix; };
 
   manual = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.manual);
+  manualEpub = (buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.manualEpub));
   manualPDF = (buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.manualPDF)).x86_64-linux;
   manpages = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.manpages);
   options = (buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.optionsJSON)).x86_64-linux;
@@ -217,6 +218,7 @@ in rec {
   tests.containers-ipv6 = callTest tests/containers-ipv6.nix {};
   tests.containers-bridge = callTest tests/containers-bridge.nix {};
   tests.containers-imperative = callTest tests/containers-imperative.nix {};
+  tests.containers-extra_veth = callTest tests/containers-extra_veth.nix {};
   tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; });
   tests.dockerRegistry = hydraJob (import tests/docker-registry.nix { system = "x86_64-linux"; });
   tests.dnscrypt-proxy = callTest tests/dnscrypt-proxy.nix { system = "x86_64-linux"; };
diff --git a/nixos/tests/containers-bridge.nix b/nixos/tests/containers-bridge.nix
index 8c3340b60a7c..bb32d852a6f5 100644
--- a/nixos/tests/containers-bridge.nix
+++ b/nixos/tests/containers-bridge.nix
@@ -10,7 +10,7 @@ in
 import ./make-test.nix ({ pkgs, ...} : {
   name = "containers-bridge";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ aristid aszlig eelco chaoflow ];
+    maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ];
   };
 
   machine =
diff --git a/nixos/tests/containers-extra_veth.nix b/nixos/tests/containers-extra_veth.nix
new file mode 100644
index 000000000000..2a54b1d961c8
--- /dev/null
+++ b/nixos/tests/containers-extra_veth.nix
@@ -0,0 +1,103 @@
+# Test for NixOS' container support.
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "containers-bridge";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ kampfschlaefer ];
+  };
+
+  machine =
+    { config, pkgs, ... }:
+    { imports = [ ../modules/installer/cd-dvd/channel.nix ];
+      virtualisation.writableStore = true;
+      virtualisation.memorySize = 768;
+      virtualisation.vlans = [];
+
+      networking.bridges = {
+        br0 = {
+          interfaces = [];
+        };
+        br1 = { interfaces = []; };
+      };
+      networking.interfaces = {
+        br0 = {
+          ip4 = [{ address = "192.168.0.1"; prefixLength = 24; }];
+          ip6 = [{ address = "fc00::1"; prefixLength = 7; }];
+        };
+        br1 = {
+          ip4 = [{ address = "192.168.1.1"; prefixLength = 24; }];
+        };
+      };
+
+      containers.webserver =
+        {
+          autoStart = true;
+          privateNetwork = true;
+          hostBridge = "br0";
+          localAddress = "192.168.0.100/24";
+          localAddress6 = "fc00::2/7";
+          extraVeths = {
+            veth1 = { hostBridge = "br1"; localAddress = "192.168.1.100/24"; };
+            veth2 = { hostAddress = "192.168.2.1"; localAddress = "192.168.2.100"; };
+          };
+          config =
+            {
+              networking.firewall.allowedTCPPorts = [ 80 ];
+              networking.firewall.allowPing = true;
+            };
+        };
+
+      virtualisation.pathsInNixDB = [ pkgs.stdenv ];
+    };
+
+  testScript =
+    ''
+      $machine->waitForUnit("default.target");
+      $machine->succeed("nixos-container list") =~ /webserver/ or die;
+
+      # Status of the webserver container.
+      $machine->succeed("nixos-container status webserver") =~ /up/ or die;
+
+      # Debug
+      #$machine->succeed("nixos-container run webserver -- ip link >&2");
+
+      # Ensure that the veths are inside the container
+      $machine->succeed("nixos-container run webserver -- ip link show veth1") =~ /state UP/ or die;
+      $machine->succeed("nixos-container run webserver -- ip link show veth2") =~ /state UP/ or die;
+
+      # Debug
+      #$machine->succeed("ip link >&2");
+
+      # Ensure the presence of the extra veths
+      $machine->succeed("ip link show veth1") =~ /state UP/ or die;
+      $machine->succeed("ip link show veth2") =~ /state UP/ or die;
+
+      # Ensure the veth1 is part of br1 on the host
+      $machine->succeed("ip link show veth1") =~ /master br1/ or die;
+
+      # Debug
+      #$machine->succeed("ip -4 a >&2");
+      #$machine->succeed("ip -4 r >&2");
+      #$machine->succeed("nixos-container run webserver -- ip link >&2");
+      #$machine->succeed("nixos-container run webserver -- ip -4 a >&2");
+      #$machine->succeed("nixos-container run webserver -- ip -4 r >&2");
+
+      # Ping on main veth
+      $machine->succeed("ping -n -c 1 192.168.0.100");
+      $machine->succeed("ping6 -n -c 1 fc00::2");
+
+      # Ping on the first extra veth
+      $machine->succeed("ping -n -c 1 192.168.1.100 >&2");
+
+      # Ping on the second extra veth
+      $machine->succeed("ping -n -c 1 192.168.2.100 >&2");
+
+      # Stop the container.
+      $machine->succeed("nixos-container stop webserver");
+      $machine->fail("ping -n -c 1 192.168.1.100 >&2");
+      $machine->fail("ping -n -c 1 192.168.2.100 >&2");
+
+      # Destroying a declarative container should fail.
+      $machine->fail("nixos-container destroy webserver");
+    '';
+})
diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix
index 89babdcc4761..7e2a54976387 100644
--- a/nixos/tests/containers-imperative.nix
+++ b/nixos/tests/containers-imperative.nix
@@ -3,7 +3,7 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "containers-imperative";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ aristid aszlig eelco chaoflow ];
+    maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ];
   };
 
   machine =
diff --git a/nixos/tests/containers-ipv4.nix b/nixos/tests/containers-ipv4.nix
index 8f1ab40221a8..31d05990a679 100644
--- a/nixos/tests/containers-ipv4.nix
+++ b/nixos/tests/containers-ipv4.nix
@@ -3,7 +3,7 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "containers-ipv4";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ aristid aszlig eelco chaoflow ];
+    maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ];
   };
 
   machine =
diff --git a/nixos/tests/containers-ipv6.nix b/nixos/tests/containers-ipv6.nix
index 0c1b8e88564d..320465ebb95b 100644
--- a/nixos/tests/containers-ipv6.nix
+++ b/nixos/tests/containers-ipv6.nix
@@ -8,7 +8,7 @@ in
 import ./make-test.nix ({ pkgs, ...} : {
   name = "containers-ipv6";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ aristid aszlig eelco chaoflow ];
+    maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ];
   };
 
   machine =
diff --git a/nixos/tests/gnome3_18-gdm.nix b/nixos/tests/gnome3_18-gdm.nix
deleted file mode 100644
index 8b1e9afedfb9..000000000000
--- a/nixos/tests/gnome3_18-gdm.nix
+++ /dev/null
@@ -1,41 +0,0 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "gnome3-gdm";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ lethalman ];
-  };
-
-  machine =
-    { config, pkgs, ... }:
-
-    { imports = [ ./common/user-account.nix ];
-
-      services.xserver.enable = true;
-
-      services.xserver.displayManager.gdm = {
-        enable = true;
-        autoLogin = {
-          enable = true;
-          user = "alice";
-        };
-      };
-      services.xserver.desktopManager.gnome3.enable = true;
-      environment.gnome3.packageSet = pkgs.gnome3_20;
-
-      virtualisation.memorySize = 512;
-    };
-
-  testScript =
-    ''
-      $machine->waitForX;
-      $machine->sleep(15);
-
-      # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
-
-      $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
-      $machine->succeed("xauth merge ~alice/.Xauthority");
-      $machine->waitForWindow(qr/Terminal/);
-      $machine->sleep(20);
-      $machine->screenshot("screen");
-    '';
-})
diff --git a/nixos/tests/gnome3_18.nix b/nixos/tests/gnome3_18.nix
deleted file mode 100644
index 2c88e6abe890..000000000000
--- a/nixos/tests/gnome3_18.nix
+++ /dev/null
@@ -1,38 +0,0 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "gnome3";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ domenkozar eelco chaoflow lethalman ];
-  };
-
-  machine =
-    { config, pkgs, ... }:
-
-    { imports = [ ./common/user-account.nix ];
-
-      services.xserver.enable = true;
-
-      services.xserver.displayManager.auto.enable = true;
-      services.xserver.displayManager.auto.user = "alice";
-      services.xserver.desktopManager.gnome3.enable = true;
-
-      environment.gnome3.packageSet = pkgs.gnome3_18;
-
-      virtualisation.memorySize = 512;
-    };
-
-  testScript =
-    ''
-      $machine->waitForX;
-      $machine->sleep(15);
-
-      # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
-
-      $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
-      $machine->succeed("xauth merge ~alice/.Xauthority");
-      $machine->waitForWindow(qr/Terminal/);
-      $machine->mustSucceed("timeout 900 bash -c 'journalctl -f|grep -m 1 \"GNOME Shell started\"'");
-      $machine->sleep(10);
-      $machine->screenshot("screen");
-    '';
-})