summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorVladimír Čunát <v@cunat.cz>2020-10-28 18:48:18 +0100
committerVladimír Čunát <v@cunat.cz>2020-10-28 18:48:18 +0100
commit31839c352e16c3eb55ee395fc127e300ff1f4b16 (patch)
tree27de3985ac9f0af77778e565673a12f211bd9bf7 /nixos
parent8c9bb91c348884964e64440867311ea234b9680e (diff)
parent05f6de94cf6c918ca1b573da2cb3dcc79851b64e (diff)
downloadnixpkgs-31839c352e16c3eb55ee395fc127e300ff1f4b16.tar
nixpkgs-31839c352e16c3eb55ee395fc127e300ff1f4b16.tar.gz
nixpkgs-31839c352e16c3eb55ee395fc127e300ff1f4b16.tar.bz2
nixpkgs-31839c352e16c3eb55ee395fc127e300ff1f4b16.tar.lz
nixpkgs-31839c352e16c3eb55ee395fc127e300ff1f4b16.tar.xz
nixpkgs-31839c352e16c3eb55ee395fc127e300ff1f4b16.tar.zst
nixpkgs-31839c352e16c3eb55ee395fc127e300ff1f4b16.zip
Merge branch 'master' into staging-next
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/user-mgmt.xml2
-rw-r--r--nixos/doc/manual/installation/upgrading.xml16
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.xml801
-rw-r--r--nixos/lib/test-driver/test-driver.py2
-rw-r--r--nixos/lib/testing-python.nix266
-rw-r--r--nixos/modules/config/system-path.nix1
-rw-r--r--nixos/modules/config/users-groups.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix9
-rw-r--r--nixos/modules/installer/tools/nixos-build-vms/build-vms.nix9
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/programs/wshowkeys.nix22
-rw-r--r--nixos/modules/security/acme.nix4
-rw-r--r--nixos/modules/security/pam.nix45
-rw-r--r--nixos/modules/services/backup/syncoid.nix52
-rw-r--r--nixos/modules/services/desktops/flatpak.nix14
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix3
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix5
-rw-r--r--nixos/modules/services/networking/mosquitto.nix36
-rw-r--r--nixos/modules/services/web-apps/engelsystem.nix2
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix25
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix1
-rw-r--r--nixos/modules/testing/test-instrumentation.nix24
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix21
-rw-r--r--nixos/tests/bitcoind.nix8
-rw-r--r--nixos/tests/caddy.nix6
-rw-r--r--nixos/tests/cadvisor.nix6
-rw-r--r--nixos/tests/cfssl.nix2
-rw-r--r--nixos/tests/common/acme/server/README.md21
-rw-r--r--nixos/tests/common/acme/server/acme.test.cert.pem19
-rw-r--r--nixos/tests/common/acme/server/acme.test.key.pem27
-rw-r--r--nixos/tests/common/acme/server/ca.cert.pem20
-rw-r--r--nixos/tests/common/acme/server/ca.key.pem27
-rw-r--r--nixos/tests/common/acme/server/default.nix5
-rw-r--r--nixos/tests/common/acme/server/generate-certs.nix29
-rw-r--r--nixos/tests/common/acme/server/snakeoil-certs.nix32
-rw-r--r--nixos/tests/convos.nix2
-rw-r--r--nixos/tests/corerad.nix2
-rw-r--r--nixos/tests/docker-edge.nix4
-rw-r--r--nixos/tests/docker-tools.nix2
-rw-r--r--nixos/tests/go-neb.nix2
-rw-r--r--nixos/tests/hadoop/hdfs.nix4
-rw-r--r--nixos/tests/hadoop/yarn.nix4
-rw-r--r--nixos/tests/haproxy.nix6
-rw-r--r--nixos/tests/hitch/default.nix2
-rw-r--r--nixos/tests/hound.nix2
-rw-r--r--nixos/tests/leaps.nix2
-rw-r--r--nixos/tests/limesurvey.nix2
-rw-r--r--nixos/tests/mailcatcher.nix2
-rw-r--r--nixos/tests/matrix-synapse.nix4
-rw-r--r--nixos/tests/mediawiki.nix2
-rw-r--r--nixos/tests/metabase.nix2
-rw-r--r--nixos/tests/morty.nix2
-rw-r--r--nixos/tests/neo4j.nix2
-rw-r--r--nixos/tests/nzbget.nix2
-rw-r--r--nixos/tests/oci-containers.nix2
-rw-r--r--nixos/tests/osrm-backend.nix4
-rw-r--r--nixos/tests/paperless.nix6
-rw-r--r--nixos/tests/peerflix.nix2
-rw-r--r--nixos/tests/php/fpm.nix2
-rw-r--r--nixos/tests/php/httpd.nix2
-rw-r--r--nixos/tests/php/pcre.nix2
-rw-r--r--nixos/tests/prometheus.nix4
-rw-r--r--nixos/tests/sanoid.nix9
-rw-r--r--nixos/tests/service-runner.nix2
-rw-r--r--nixos/tests/spacecookie.nix4
-rw-r--r--nixos/tests/sslh.nix2
-rw-r--r--nixos/tests/sympa.nix2
-rw-r--r--nixos/tests/syncthing-relay.nix2
-rw-r--r--nixos/tests/syncthing.nix4
-rw-r--r--nixos/tests/trac.nix2
-rw-r--r--nixos/tests/trezord.nix2
-rw-r--r--nixos/tests/trickster.nix8
-rw-r--r--nixos/tests/upnp.nix2
-rw-r--r--nixos/tests/uwsgi.nix2
-rw-r--r--nixos/tests/victoriametrics.nix6
-rw-r--r--nixos/tests/web-servers/unit-php.nix2
-rw-r--r--nixos/tests/wordpress.nix2
78 files changed, 1300 insertions, 395 deletions
diff --git a/nixos/doc/manual/configuration/user-mgmt.xml b/nixos/doc/manual/configuration/user-mgmt.xml
index cbec83814c9..e83e7b75ef5 100644
--- a/nixos/doc/manual/configuration/user-mgmt.xml
+++ b/nixos/doc/manual/configuration/user-mgmt.xml
@@ -38,7 +38,7 @@
   assigned by setting the user's
   <link linkend="opt-users.users._name_.hashedPassword">hashedPassword</link>
   option. A hashed password can be generated using <command>mkpasswd -m
-  sha-512</command> after installing the <literal>mkpasswd</literal> package.
+  sha-512</command>.
  </para>
  <para>
   A user ID (uid) is assigned automatically. You can also specify a uid
diff --git a/nixos/doc/manual/installation/upgrading.xml b/nixos/doc/manual/installation/upgrading.xml
index 08780051d5f..15ba5db9a37 100644
--- a/nixos/doc/manual/installation/upgrading.xml
+++ b/nixos/doc/manual/installation/upgrading.xml
@@ -14,7 +14,7 @@
     <para>
      <emphasis>Stable channels</emphasis>, such as
      <literal
-    xlink:href="https://nixos.org/channels/nixos-20.03">nixos-20.03</literal>.
+    xlink:href="https://nixos.org/channels/nixos-20.09">nixos-20.09</literal>.
      These only get conservative bug fixes and package upgrades. For instance,
      a channel update may cause the Linux kernel on your system to be upgraded
      from 4.19.34 to 4.19.38 (a minor bug fix), but not from
@@ -38,7 +38,7 @@
     <para>
      <emphasis>Small channels</emphasis>, such as
      <literal
-    xlink:href="https://nixos.org/channels/nixos-20.03-small">nixos-20.03-small</literal>
+    xlink:href="https://nixos.org/channels/nixos-20.09-small">nixos-20.09-small</literal>
      or
      <literal
     xlink:href="https://nixos.org/channels/nixos-unstable-small">nixos-unstable-small</literal>.
@@ -63,8 +63,8 @@
  <para>
   When you first install NixOS, you’re automatically subscribed to the NixOS
   channel that corresponds to your installation source. For instance, if you
-  installed from a 20.03 ISO, you will be subscribed to the
-  <literal>nixos-20.03</literal> channel. To see which NixOS channel you’re
+  installed from a 20.09 ISO, you will be subscribed to the
+  <literal>nixos-20.09</literal> channel. To see which NixOS channel you’re
   subscribed to, run the following as root:
 <screen>
 <prompt># </prompt>nix-channel --list | grep nixos
@@ -75,13 +75,13 @@ nixos https://nixos.org/channels/nixos-unstable
 <prompt># </prompt>nix-channel --add https://nixos.org/channels/<replaceable>channel-name</replaceable> nixos
 </screen>
   (Be sure to include the <literal>nixos</literal> parameter at the end.) For
-  instance, to use the NixOS 20.03 stable channel:
+  instance, to use the NixOS 20.09 stable channel:
 <screen>
-<prompt># </prompt>nix-channel --add https://nixos.org/channels/nixos-20.03 nixos
+<prompt># </prompt>nix-channel --add https://nixos.org/channels/nixos-20.09 nixos
 </screen>
   If you have a server, you may want to use the “small” channel instead:
 <screen>
-<prompt># </prompt>nix-channel --add https://nixos.org/channels/nixos-20.03-small nixos
+<prompt># </prompt>nix-channel --add https://nixos.org/channels/nixos-20.09-small nixos
 </screen>
   And if you want to live on the bleeding edge:
 <screen>
@@ -132,7 +132,7 @@ nixos https://nixos.org/channels/nixos-unstable
    kernel, initrd or kernel modules.
    You can also specify a channel explicitly, e.g.
 <programlisting>
-<xref linkend="opt-system.autoUpgrade.channel"/> = https://nixos.org/channels/nixos-20.03;
+<xref linkend="opt-system.autoUpgrade.channel"/> = https://nixos.org/channels/nixos-20.09;
 </programlisting>
   </para>
  </section>
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index 782227de06f..c399c22eef5 100644
--- a/nixos/doc/manual/release-notes/rl-2009.xml
+++ b/nixos/doc/manual/release-notes/rl-2009.xml
@@ -3,8 +3,11 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="sec-release-20.09">
- <title>Release 20.09 (“Nightingale”, 2020.09/??)</title>
+ <title>Release 20.09 (“Nightingale”, 2020.10/27)</title>
 
+  <para>
+   Support is planned until the end of April 2021, handing over to 21.03.
+  </para>
  <section xmlns="http://docbook.org/ns/docbook"
          xmlns:xlink="http://www.w3.org/1999/xlink"
          xmlns:xi="http://www.w3.org/2001/XInclude"
@@ -13,88 +16,601 @@
   <title>Highlights</title>
 
   <para>
-   In addition to numerous new and upgraded packages, this release has the
+   In addition to 7349 new, 14442 updated, and 8181 removed packages, this release has the
    following highlights:
   </para>
 
   <itemizedlist>
    <listitem>
     <para>
-     Support is planned until the end of April 2021, handing over to 21.03.
+     Core version changes:
     </para>
-   </listitem>
-   <listitem>
-    <para>GNOME desktop environment was upgraded to 3.36, see its <link xlink:href="https://help.gnome.org/misc/release-notes/3.36/">release notes</link>.</para>
-   </listitem>
-   <listitem>
-     <para>
-     The Cinnamon desktop environment (v4.6) has been added. <varname>services.xserver.desktopManager.cinnamon.enable = true;</varname> to try it out!
-     Remember that, with any new feature it's possible you could run into issues, so please send all support requests to <link xlink:href="https://github.com/NixOS/nixpkgs/issues">github.com/NixOS/nixpkgs</link> to notify the maintainers.
-     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       gcc: 9.2.0 -> 9.3.0
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       glibc: 2.30 -> 2.31
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       linux: still defaults to 5.4.x, all supported kernels available
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       mesa: 19.3.5 -> 20.1.7
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
    <listitem>
     <para>
-      Quickly configure a complete, private, self-hosted video
-      conferencing solution with the new Jitsi Meet module.
+     Desktop Environments:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       plasma5: 5.17.5 -> 5.18.5
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       kdeApplications: 19.12.3 -> 20.08.1
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       gnome3: 3.34 -> 3.36, see its <link xlink:href="https://help.gnome.org/misc/release-notes/3.36/">release notes</link>
+      </para>
+     </listitem>
+      <listitem>
+       <para>
+        cinnamon: added at 4.6
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       NixOS now distributes an official <link xlink:href="https://nixos.org/download.html#nixos-iso">GNOME ISO</link>
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
+
    <listitem>
     <para>
-    <package>maxx</package> package removed along with <varname>services.xserver.desktopManager.maxx</varname> module.
-    Please migrate to <package>cdesktopenv</package> and <varname>services.xserver.desktopManager.cde</varname> module.
+     Programming Languages and Frameworks:
     </para>
+    <itemizedlist>
+
+     <listitem>
+      <para>
+       Agda ecosystem was heavily reworked (see more details below)
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       PHP now defaults to PHP 7.4, updated from 7.3
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       PHP 7.2 is no longer supported due to upstream not supporting this version for the entire lifecycle of the 20.09 release
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Python 3 now defaults to Python 3.8 instead of 3.7
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Python 3.5 reached its upstream EOL at the end of September 2020: it
+       has been removed from the list of available packages
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
+
    <listitem>
     <para>
-     We now distribute a GNOME ISO.
+     Databases and Service Monitoring:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       MariaDB has been updated to 10.4, MariaDB Galera to 26.4. Please read the related upgrade instructions under <link linkend="sec-release-20.09-incompatibilities">backwards incompatibilities</link> before upgrading.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+        Zabbix now defaults to 5.0, updated from 4.4. Please read related sections under <link linkend="sec-release-20.09-incompatibilities">backwards compatibilities</link> before upgrading.
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
+
    <listitem>
     <para>
-     PHP now defaults to PHP 7.4, updated from 7.3.
+     Major module changes:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       Quickly configure a complete, private, self-hosted video
+       conferencing solution with the new Jitsi Meet module.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Two new options, <link linkend="opt-services.openssh.authorizedKeysCommand">authorizedKeysCommand</link>
+       and <link linkend="opt-services.openssh.authorizedKeysCommandUser">authorizedKeysCommandUser</link>, have
+       been added to the <literal>openssh</literal> module. If you have <literal>AuthorizedKeysCommand</literal>
+       in your <link linkend="opt-services.openssh.extraConfig">services.openssh.extraConfig</link> you should
+       make use of these new options instead.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       There is a new module for Podman (<varname>virtualisation.podman</varname>), a drop-in replacement for the Docker command line.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       The new <varname>virtualisation.containers</varname> module manages configuration shared by the CRI-O and Podman modules.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+        Declarative Docker containers are renamed from <varname>docker-containers</varname> to <varname>virtualisation.oci-containers.containers</varname>.
+        This is to make it possible to use <literal>podman</literal> instead of <literal>docker</literal>.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+        The new option <link linkend="opt-documentation.man.generateCaches">documentation.man.generateCaches</link>
+        has been added to automatically generate the <literal>man-db</literal> caches, which are needed by utilities
+        like <command>whatis</command> and <command>apropos</command>. The caches are generated during the build of
+        the NixOS configuration: since this can be expensive when a large number of packages are installed, the
+        feature is disabled by default.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <varname>services.postfix.sslCACert</varname> was replaced by <varname>services.postfix.tlsTrustedAuthorities</varname> which now defaults to system certificate authorities.
+      </para>
+     </listitem>
+     <listitem>
+       <para>
+         The various documented workarounds to use steam have been converted to a module. <varname>programs.steam.enable</varname> enables steam, controller support and the workarounds.
+       </para>
+     </listitem>
+     <listitem>
+       <para>
+         Support for built-in LCDs in various pieces of Logitech hardware (keyboards and USB speakers). <varname>hardware.logitech.lcd.enable</varname> enables support for all hardware supported by the <link xlink:href="https://sourceforge.net/projects/g15daemon/">g15daemon project</link>.
+       </para>
+     </listitem>
+     <listitem>
+       <para>
+         The GRUB module gained support for basic password protection, which
+         allows to restrict non-default entries in the boot menu to one or more
+         users. The users and passwords are defined via the option
+         <option>boot.loader.grub.users</option>.
+         Note: Password support is only available in GRUB version 2.
+       </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
+
    <listitem>
     <para>
-     PHP 7.2 is no longer supported due to upstream not supporting this version for the entire lifecycle of the 20.09 release.
+     NixOS module changes:
     </para>
+    <itemizedlist>
+     <listitem>
+       <para>
+        The NixOS module system now supports freeform modules as a mix between <literal>types.attrsOf</literal> and <literal>types.submodule</literal>. These allow you to explicitly declare a subset of options while still permitting definitions without an associated option. See <xref linkend='sec-freeform-modules'/> for how to use them.
+       </para>
+     </listitem>
+     <listitem>
+      <para>
+       Following its deprecation in 20.03, the Perl NixOS test driver has been removed.
+       All remaining tests have been ported to the Python test framework.
+       Code outside nixpkgs using <filename>make-test.nix</filename> or
+       <filename>testing.nix</filename> needs to be ported to
+       <filename>make-test-python.nix</filename> and
+       <filename>testing-python.nix</filename> respectively.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+        Subordinate GID and UID mappings are now set up automatically for all normal users.
+        This will make container tools like Podman work as non-root users out of the box.
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
+
+  </itemizedlist>
+ </section>
+
+ <section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.09-new-services">
+  <title>New Services</title>
+
+  <para>
+   In addition to 1119 new, 118 updated, and 476 removed options; 61 new modules were added since the last release:
+  </para>
+
+  <itemizedlist>
    <listitem>
     <para>
-     Python 3 now defaults to Python 3.8 instead of 3.7.
+       Hardware:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <xref linkend="opt-hardware.system76.firmware-daemon.enable" /> adds easy support of system76 firmware
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-hardware.uinput.enable" /> loads uinput kernel module
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-hardware.video.hidpi.enable" /> enable good defaults for HiDPI displays
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-hardware.wooting.enable" /> support for Wooting keyboards
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-hardware.xpadneo.enable" /> xpadneo driver for Xbox One wireless controllers
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
    <listitem>
     <para>
-     Python 3.5 has reached its upstream EOL at the end of September 2020: it
-     has been removed from the list of available packages.
+       Programs:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <xref linkend="opt-programs.hamster.enable" /> enable hamster time tracking
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-programs.steam.enable" /> adds easy enablement of steam and related system configuration
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
    <listitem>
     <para>
-     Two new options, <link linkend="opt-services.openssh.authorizedKeysCommand">authorizedKeysCommand</link>
-     and <link linkend="opt-services.openssh.authorizedKeysCommandUser">authorizedKeysCommandUser</link>, have
-     been added to the <literal>openssh</literal> module. If you have <literal>AuthorizedKeysCommand</literal>
-     in your <link linkend="opt-services.openssh.extraConfig">services.openssh.extraConfig</link> you should
-     make use of these new options instead.
+       Security:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <xref linkend="opt-security.doas.enable" /> alternative to sudo, allows non-root users to execute commands as root
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-security.tpm2.enable" /> add Trusted Platform Module 2 support
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
    <listitem>
     <para>
-     There is a new module for Podman(<varname>virtualisation.podman</varname>), a drop-in replacement for the Docker command line.
+       System:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <xref linkend="opt-boot.initrd.network.openvpn.enable" /> start an OpenVPN client during initrd boot
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
    <listitem>
     <para>
-     The new <varname>virtualisation.containers</varname> module manages configuration shared by the CRI-O and Podman modules.
+       Virtualization:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <xref linkend="opt-boot.enableContainers" /> use nixos-containers
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-virtualisation.oci-containers.containers" /> run OCI (Docker) containers
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-virtualisation.podman.enable" /> daemonless container engine
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
+
    <listitem>
     <para>
-      Declarative Docker containers are renamed from <varname>docker-containers</varname> to <varname>virtualisation.oci-containers.containers</varname>.
-      This is to make it possible to use <literal>podman</literal> instead of <literal>docker</literal>.
+       Services:
     </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.ankisyncd.enable" /> Anki sync server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.bazarr.enable" /> Subtitle manager for Sonarr and Radarr
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.biboumi.enable" /> Biboumi XMPP gateway to IRC
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.blockbook-frontend" /> Blockbook-frontend, a service for the Trezor wallet
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.cage.enable" /> Wayland cage service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.convos.enable" /> IRC daemon, which can be accessed throught the browser
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.engelsystem.enable" /> Tool for coordinating volunteers and shifts on large events
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.espanso.enable" /> text-expander written in rust
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.foldingathome.enable" /> Folding@home client
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.gerrit.enable" /> Web-based team code collaboration tool
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.go-neb.enable" /> Matrix bot
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.hardware.xow.enable" /> xow as a systemd service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.hercules-ci-agent.enable" /> Hercules CI build agent
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.jicofo.enable" /> Jitsi Conference Focus, component of Jitsi Meet
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.jirafeau.enable" /> A web file repository
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.jitsi-meet.enable" /> Secure, simple and scalable video conferences
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.jitsi-videobridge.enable" /> Jitsi Videobridge, a WebRTC compatible router
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.jupyterhub.enable" /> Jupyterhub development server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.k3s.enable" /> Lightweight Kubernetes distribution
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.magic-wormhole-mailbox-server.enable" /> Magic Wormhole Mailbox Server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.malcontent.enable" /> Parental Control support
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.matrix-appservice-discord.enable" /> Matrix and Discord bridge
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.mautrix-telegram.enable" /> Matrix-Telegram puppeting/relaybot bridge
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.mirakurun.enable" /> Japanese DTV Tuner Server Service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.molly-brown.enable" /> Molly-Brown Gemini server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.mullvad-vpn.enable" /> Mullvad VPN daemon
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.ncdns.enable" /> Namecoin to DNS bridge
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.nextdns.enable" /> NextDNS to DoH Proxy service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.nix-store-gcs-proxy" /> Google storage bucket to be used as a nix store
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.onedrive.enable" /> OneDrive sync service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.pinnwand.enable" /> Pastebin-like service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.pixiecore.enable" /> Manage network booting of machines
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.privacyidea.enable" /> Privacy authentication server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.quorum.enable" /> Quorum blockchain daemon
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.robustirc-bridge.enable" /> RobustIRC bridge
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.rss-bridge.enable" /> Generate RSS and Atom feeds
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.rtorrent.enable" /> rTorrent service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.smartdns.enable" /> SmartDNS DNS server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.sogo.enable" /> SOGo groupware
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.teeworlds.enable" /> Teeworlds game server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.torque.mom.enable" /> torque computing node
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.torque.server.enable" /> torque server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.tuptime.enable" /> A total uptime service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.urserver.enable" /> X11 remote server
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.wasabibackend.enable" /> Wasabi backend service
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.yubikey-agent.enable" /> Yubikey agent
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <xref linkend="opt-services.zigbee2mqtt.enable" /> Zigbee to MQTT bridge
+      </para>
+     </listitem>
+    </itemizedlist>
    </listitem>
+
+  </itemizedlist>
+
+ </section>
+
+ <section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.09-incompatibilities">
+  <title>Backward Incompatibilities</title>
+
+  <para>
+   When upgrading from a previous release, please be aware of the following
+   incompatible changes:
+  </para>
+
+  <itemizedlist>
    <listitem>
     <para>
       MariaDB has been updated to 10.4, MariaDB Galera to 26.4.
@@ -144,36 +660,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
       from the default of <literal>mysql</literal> to a different user please change <literal>'mysql'@'localhost'</literal> to the corresponding user instead.
     </para>
    </listitem>
-   <listitem>
-    <para>
-      The new option <link linkend="opt-documentation.man.generateCaches">documentation.man.generateCaches</link>
-      has been added to automatically generate the <literal>man-db</literal> caches, which are needed by utilities
-      like <command>whatis</command> and <command>apropos</command>. The caches are generated during the build of
-      the NixOS configuration: since this can be expensive when a large number of packages are installed, the
-      feature is disabled by default.
-    </para>
-   </listitem>
-   <listitem>
-    <para>
-     <varname>services.postfix.sslCACert</varname> was replaced by <varname>services.postfix.tlsTrustedAuthorities</varname> which now defaults to system certificate authorities.
-    </para>
-   </listitem>
-   <listitem>
-    <para>
-      Subordinate GID and UID mappings are now set up automatically for all normal users.
-      This will make container tools like Podman work as non-root users out of the box.
-    </para>
-   </listitem>
-   <listitem>
-     <para>
-       The various documented workarounds to use steam have been converted to a module. <varname>programs.steam.enable</varname> enables steam, controller support and the workarounds.
-     </para>
-   </listitem>
-   <listitem>
-     <para>
-       Support for built-in LCDs in various pieces of Logitech hardware (keyboards and USB speakers). <varname>hardware.logitech.lcd.enable</varname> enables support for all hardware supported by the g15daemon project.
-     </para>
-   </listitem>
+
    <listitem>
     <para>
       Zabbix now defaults to 5.0, updated from 4.4. Please carefully read through
@@ -208,72 +695,13 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
 </programlisting>
     </para>
    </listitem>
-   <listitem>
-     <para>
-      The NixOS module system now supports freeform modules as a mix between <literal>types.attrsOf</literal> and <literal>types.submodule</literal>. These allow you to explicitly declare a subset of options while still permitting definitions without an associated option. See <xref linkend='sec-freeform-modules'/> for how to use them.
-     </para>
-   </listitem>
-   <listitem>
-     <para>
-       The GRUB module gained support for basic password protection, which
-       allows to restrict non-default entries in the boot menu to one or more
-       users. The users and passwords are defined via the option
-       <option>boot.loader.grub.users</option>.
-       Note: Password support is only avaiable in GRUB version 2.
-     </para>
-   </listitem>
-   <listitem>
-     <para>
-       Following its deprecation in 20.03, the Perl NixOS test driver has been removed.
-       All remaining tests have been ported to the Python test framework.
-       Code outside nixpkgs using <filename>make-test.nix</filename> or
-       <filename>testing.nix</filename> needs to be ported to
-       <filename>make-test-python.nix</filename> and
-       <filename>testing-python.nix</filename> respectively.
-     </para>
-   </listitem>
-  </itemizedlist>
- </section>
-
- <section xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="sec-release-20.09-new-services">
-  <title>New Services</title>
 
-  <para>
-   The following new services were added since the last release:
-  </para>
-
-  <itemizedlist>
    <listitem>
     <para>
-      There is a new <xref linkend="opt-security.doas.enable"/> module that provides <command>doas</command>, a lighter alternative to <command>sudo</command> with many of the same features.
-    </para>
-  </listitem>
-  <listitem>
-    <para>
-      <link xlink:href="https://hercules-ci.com">Hercules CI</link> Agent is a specialized build agent for projects built with Nix. See the <link xlink:href="https://nixos.org/nixos/options.html#services.hercules-ci-agent">options</link> and <link xlink:href="https://docs.hercules-ci.com/hercules-ci/getting-started/#deploy-agent">setup</link>.
+    <package>maxx</package> package removed along with <varname>services.xserver.desktopManager.maxx</varname> module.
+    Please migrate to <package>cdesktopenv</package> and <varname>services.xserver.desktopManager.cde</varname> module.
     </para>
    </listitem>
-  </itemizedlist>
-
- </section>
-
- <section xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="sec-release-20.09-incompatibilities">
-  <title>Backward Incompatibilities</title>
-
-  <para>
-   When upgrading from a previous release, please be aware of the following
-   incompatible changes:
-  </para>
-
-  <itemizedlist>
    <listitem>
     <para>
      The <link linkend="opt-services.matrix-synapse.enable">matrix-synapse</link> module no longer includes optional dependencies by default, they have to be added through the <link linkend="opt-services.matrix-synapse.plugins">plugins</link> option.
@@ -300,7 +728,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
      It can still be enabled by providing <literal>phantomJsSupport = true</literal> to the package instantiation:
 <programlisting>{
   services.grafana.package = pkgs.grafana.overrideAttrs (oldAttrs: rec {
-    phantomJsSupport = false;
+    phantomJsSupport = true;
   });
 }</programlisting>
     </para>
@@ -1235,4 +1663,131 @@ services.transmission.settings.rpc-bind-address = "0.0.0.0";
     </listitem>
   </itemizedlist>
  </section>
+
+ <section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.09-contributions">
+  <title>Contributions</title>
+  <para>
+        I, Jonathan Ringer, would like to thank the following individuals for their work on nixpkgs. This release could not be done without the hard work of the NixOS community. There were 31282 contributions across 1313 contributors.
+  </para>
+  <orderedlist>
+   <para>
+    Top contributors to NixOS/Nixpkgs from the 20.03 release to the 20.09 release:
+   </para>
+   <listitem>
+    <para>
+  2288  Mario Rodas
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+  1837  Frederik Rietdijk
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   946  Jörg Thalheim
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   925  Maximilian Bosch
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   687  Jonathan Ringer
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   651  Jan Tojnar
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   622  Daniël de Kok
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   605  WORLDofPEACE
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   597  Florian Klink
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   528  José Romildo Malaquias
+    </para>
+   </listitem>
+  </orderedlist>
+
+  <orderedlist>
+   <para>
+    Top contributors to stabilizing this release (Zero Hydra Failures period):
+   </para>
+   <listitem>
+    <para>
+      281  volth
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+   101  Robert Scott
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    86  Tim Steinbach
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    76  WORLDofPEACE
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    49  Maximilian Bosch
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    42  Thomas Tuegel
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    37  Doron Behar
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    36  Vladimír Čunát
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    27  Jonathan Ringer
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+    27  Maciej Krüger
+    </para>
+   </listitem>
+  </orderedlist>
+
+  <para>
+   I, Jonathan Ringer, would also like to personally thank @WORLDofPEACE for their help in mentoring me on the release process. Special thanks also goes to Thomas Tuegel for helping immensely with stabilizing Qt, KDE, and Plasma5; I would also like to thank Robert Scott for his numerous fixes and pull request reviews.
+   </para>
+
+ </section>
 </section>
diff --git a/nixos/lib/test-driver/test-driver.py b/nixos/lib/test-driver/test-driver.py
index 156392ad1e3..68bd35dd98e 100644
--- a/nixos/lib/test-driver/test-driver.py
+++ b/nixos/lib/test-driver/test-driver.py
@@ -110,7 +110,6 @@ def create_vlan(vlan_nr: str) -> Tuple[str, str, "subprocess.Popen[bytes]", Any]
     pty_master, pty_slave = pty.openpty()
     vde_process = subprocess.Popen(
         ["vde_switch", "-s", vde_socket, "--dirmode", "0700"],
-        bufsize=1,
         stdin=pty_slave,
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
@@ -748,7 +747,6 @@ class Machine:
 
         self.process = subprocess.Popen(
             self.script,
-            bufsize=1,
             stdin=subprocess.DEVNULL,
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix
index 302c7f78bf8..13abfb9a111 100644
--- a/nixos/lib/testing-python.nix
+++ b/nixos/lib/testing-python.nix
@@ -3,13 +3,13 @@
   # Use a minimal kernel?
 , minimal ? false
   # Ignored
-, config ? {}
+, config ? { }
   # !!! See comment about args in lib/modules.nix
-, specialArgs ? {}
+, specialArgs ? { }
   # Modules to add to each VM
-, extraConfigurations ? [] }:
+, extraConfigurations ? [ ]
+}:
 
-with import ./build-vms.nix { inherit system pkgs minimal specialArgs extraConfigurations; };
 with pkgs;
 
 rec {
@@ -17,42 +17,41 @@ rec {
   inherit pkgs;
 
 
-  mkTestDriver = let
-    testDriverScript = ./test-driver/test-driver.py;
-  in qemu_pkg: stdenv.mkDerivation {
-    name = "nixos-test-driver";
-
-    nativeBuildInputs = [ makeWrapper ];
-    buildInputs = [ (python3.withPackages (p: [ p.ptpython ])) ];
-    checkInputs = with python3Packages; [ pylint black mypy ];
-
-    dontUnpack = true;
+  mkTestDriver =
+    let
+      testDriverScript = ./test-driver/test-driver.py;
+    in
+    qemu_pkg: stdenv.mkDerivation {
+      name = "nixos-test-driver";
 
-    preferLocalBuild = true;
+      nativeBuildInputs = [ makeWrapper ];
+      buildInputs = [ (python3.withPackages (p: [ p.ptpython ])) ];
+      checkInputs = with python3Packages; [ pylint black mypy ];
 
-    doCheck = true;
-    checkPhase = ''
-      mypy --disallow-untyped-defs \
-           --no-implicit-optional \
-           --ignore-missing-imports ${testDriverScript}
-      pylint --errors-only ${testDriverScript}
-      black --check --diff ${testDriverScript}
-    '';
+      dontUnpack = true;
 
-    installPhase =
-      ''
-        mkdir -p $out/bin
-        cp ${testDriverScript} $out/bin/nixos-test-driver
-        chmod u+x $out/bin/nixos-test-driver
-        # TODO: copy user script part into this file (append)
+      preferLocalBuild = true;
 
-        wrapProgram $out/bin/nixos-test-driver \
-          --prefix PATH : "${lib.makeBinPath [ qemu_pkg vde2 netpbm coreutils ]}" \
+      doCheck = true;
+      checkPhase = ''
+        mypy --disallow-untyped-defs \
+             --no-implicit-optional \
+             --ignore-missing-imports ${testDriverScript}
+        pylint --errors-only ${testDriverScript}
+        black --check --diff ${testDriverScript}
       '';
-  };
 
-  testDriver = mkTestDriver qemu_test;
-  testDriverInteractive = mkTestDriver qemu_kvm;
+      installPhase =
+        ''
+          mkdir -p $out/bin
+          cp ${testDriverScript} $out/bin/nixos-test-driver
+          chmod u+x $out/bin/nixos-test-driver
+          # TODO: copy user script part into this file (append)
+
+          wrapProgram $out/bin/nixos-test-driver \
+            --prefix PATH : "${lib.makeBinPath [ qemu_pkg vde2 netpbm coreutils ]}" \
+        '';
+    };
 
   # Run an automated test suite in the given virtual network.
   # `driver' is the script that runs the network.
@@ -75,11 +74,10 @@ rec {
     { testScript
     , enableOCR ? false
     , name ? "unnamed"
-    # Skip linting (mainly intended for faster dev cycles)
+      # Skip linting (mainly intended for faster dev cycles)
     , skipLint ? false
     , ...
     } @ t:
-
     let
       # A standard store path to the vm monitor is built like this:
       #   /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
@@ -88,25 +86,7 @@ rec {
       maxTestNameLen = 50;
       testNameLen = builtins.stringLength name;
 
-      testDriverName = with builtins;
-        if testNameLen > maxTestNameLen then
-          abort ("The name of the test '${name}' must not be longer than ${toString maxTestNameLen} " +
-            "it's currently ${toString testNameLen} characters long.")
-        else
-          "nixos-test-driver-${name}";
-
-      nodes = buildVirtualNetwork (
-        t.nodes or (if t ? machine then { machine = t.machine; } else { }));
 
-      testScript' =
-        # Call the test script with the computed nodes.
-        if lib.isFunction testScript
-        then testScript { inherit nodes; }
-        else testScript;
-
-      vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
-
-      vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
 
       ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
 
@@ -115,78 +95,124 @@ rec {
       # Generate convenience wrappers for running the test driver
       # interactively with the specified network, and for starting the
       # VMs from the command line.
-      driver = testDriver:
+      mkDriver = qemu_pkg:
         let
+          build-vms = import ./build-vms.nix {
+            inherit system pkgs minimal specialArgs;
+            extraConfigurations = extraConfigurations ++ (pkgs.lib.optional (qemu_pkg != null)
+              {
+                virtualisation.qemu.package = qemu_pkg;
+              }
+            );
+          };
+
+          # FIXME: get this pkg from the module system
+          testDriver = mkTestDriver (if qemu_pkg == null then pkgs.qemu_test else qemu_pkg);
+
+          nodes = build-vms.buildVirtualNetwork (
+            t.nodes or (if t ? machine then { machine = t.machine; } else { })
+          );
+          vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
+          vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
+
+          testScript' =
+            # Call the test script with the computed nodes.
+            if lib.isFunction testScript
+            then testScript { inherit nodes; }
+            else testScript;
+
+          testDriverName = with builtins;
+            if testNameLen > maxTestNameLen then
+              abort
+                ("The name of the test '${name}' must not be longer than ${toString maxTestNameLen} " +
+                  "it's currently ${toString testNameLen} characters long.")
+            else
+              "nixos-test-driver-${name}";
+
           warn = if skipLint then lib.warn "Linting is disabled!" else lib.id;
         in
         warn (runCommand testDriverName
-        { buildInputs = [ makeWrapper];
-          testScript = testScript';
-          preferLocalBuild = true;
-          testName = name;
-        }
-        ''
-          mkdir -p $out/bin
-
-          echo -n "$testScript" > $out/test-script
-          ${lib.optionalString (!skipLint) ''
-            ${python3Packages.black}/bin/black --check --diff $out/test-script
-          ''}
-
-          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
-          vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
-          wrapProgram $out/bin/nixos-test-driver \
-            --add-flags "''${vms[*]}" \
-            ${lib.optionalString enableOCR
-              "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
-            --run "export testScript=\"\$(${coreutils}/bin/cat $out/test-script)\"" \
-            --set VLANS '${toString vlans}'
-          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
-          wrapProgram $out/bin/nixos-run-vms \
-            --add-flags "''${vms[*]}" \
-            ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
-            --set tests 'start_all(); join_all();' \
-            --set VLANS '${toString vlans}' \
-            ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
-        ''); # "
+          {
+            buildInputs = [ makeWrapper ];
+            testScript = testScript';
+            preferLocalBuild = true;
+            testName = name;
+            passthru = {
+              inherit nodes;
+            };
+          }
+          ''
+            mkdir -p $out/bin
+
+            echo -n "$testScript" > $out/test-script
+            ${lib.optionalString (!skipLint) ''
+              ${python3Packages.black}/bin/black --check --diff $out/test-script
+            ''}
+
+            ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
+            vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
+            wrapProgram $out/bin/nixos-test-driver \
+              --add-flags "''${vms[*]}" \
+              ${lib.optionalString enableOCR
+                "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
+              --run "export testScript=\"\$(${coreutils}/bin/cat $out/test-script)\"" \
+              --set VLANS '${toString vlans}'
+            ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
+            wrapProgram $out/bin/nixos-run-vms \
+              --add-flags "''${vms[*]}" \
+              ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
+              --set tests 'start_all(); join_all();' \
+              --set VLANS '${toString vlans}' \
+              ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
+          ''); # "
 
       passMeta = drv: drv // lib.optionalAttrs (t ? meta) {
-        meta = (drv.meta or {}) // t.meta;
+        meta = (drv.meta or { }) // t.meta;
       };
 
-      test = passMeta (runTests (driver testDriver));
+      driver = mkDriver null;
+      driverInteractive = mkDriver pkgs.qemu;
 
-      nodeNames = builtins.attrNames nodes;
+      test = passMeta (runTests driver);
+
+      nodeNames = builtins.attrNames driver.nodes;
       invalidNodeNames = lib.filter
-        (node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null) nodeNames;
+        (node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null)
+        nodeNames;
 
     in
-      if lib.length invalidNodeNames > 0 then
-        throw ''
-          Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
-          All machines are referenced as python variables in the testing framework which will break the
-          script when special characters are used.
+    if lib.length invalidNodeNames > 0 then
+      throw ''
+        Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
+        All machines are referenced as python variables in the testing framework which will break the
+        script when special characters are used.
 
-          Please stick to alphanumeric chars and underscores as separation.
-        ''
-      else
-        test // {
-          inherit nodes test;
-          driver = driver testDriver;
-          driverInteractive = driver testDriverInteractive;
-        };
+        Please stick to alphanumeric chars and underscores as separation.
+      ''
+    else
+      test // {
+        inherit test driver driverInteractive;
+        inherit (driver) nodes;
+      };
 
   runInMachine =
     { drv
     , machine
     , preBuild ? ""
     , postBuild ? ""
+    , qemu ? pkgs.qemu_test
     , ... # ???
     }:
     let
-      vm = buildVM { }
-        [ machine
-          { key = "run-in-machine";
+      build-vms = import ./build-vms.nix {
+        inherit system pkgs minimal specialArgs extraConfigurations;
+      };
+
+      vm = build-vms.buildVM { }
+        [
+          machine
+          {
+            key = "run-in-machine";
             networking.hostName = "client";
             nix.readOnlyStore = false;
             virtualisation.writableStore = false;
@@ -229,20 +255,20 @@ rec {
         unset xchg
 
         export tests='${testScript}'
-        ${testDriver}/bin/nixos-test-driver ${vm.config.system.build.vm}/bin/run-*-vm
+        ${mkTestDriver qemu}/bin/nixos-test-driver --keep-vm-state ${vm.config.system.build.vm}/bin/run-*-vm
       ''; # */
 
     in
-      lib.overrideDerivation drv (attrs: {
-        requiredSystemFeatures = [ "kvm" ];
-        builder = "${bash}/bin/sh";
-        args = ["-e" vmRunCommand];
-        origArgs = attrs.args;
-        origBuilder = attrs.builder;
-      });
+    lib.overrideDerivation drv (attrs: {
+      requiredSystemFeatures = [ "kvm" ];
+      builder = "${bash}/bin/sh";
+      args = [ "-e" vmRunCommand ];
+      origArgs = attrs.args;
+      origBuilder = attrs.builder;
+    });
 
 
-  runInMachineWithX = { require ? [], ... } @ args:
+  runInMachineWithX = { require ? [ ], ... } @ args:
     let
       client =
         { ... }:
@@ -258,13 +284,13 @@ rec {
           services.xserver.windowManager.icewm.enable = true;
         };
     in
-      runInMachine ({
-        machine = client;
-        preBuild =
-          ''
-            client.wait_for_x()
-          '';
-      } // args);
+    runInMachine ({
+      machine = client;
+      preBuild =
+        ''
+          client.wait_for_x()
+        '';
+    } // args);
 
 
   simpleTest = as: (makeTest as).test;
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index c46937f8008..c65fa1a684f 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -33,6 +33,7 @@ let
       pkgs.ncurses
       pkgs.netcat
       config.programs.ssh.package
+      pkgs.mkpasswd
       pkgs.procps
       pkgs.su
       pkgs.time
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 5264d5b56fa..72285fe631d 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -35,8 +35,7 @@ let
   '';
 
   hashedPasswordDescription = ''
-    To generate a hashed password install the <literal>mkpasswd</literal>
-    package and run <literal>mkpasswd -m sha-512</literal>.
+    To generate a hashed password run <literal>mkpasswd -m sha-512</literal>.
 
     If set to an empty string (<literal>""</literal>), this user will
     be able to log in without being asked for a password (but not via remote
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
index 8c98691116d..803bae4212e 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
@@ -9,7 +9,14 @@ with lib;
 
   isoImage.edition = "gnome";
 
-  services.xserver.desktopManager.gnome3.enable = true;
+  services.xserver.desktopManager.gnome3 = {
+    # Add firefox to favorite-apps
+    favoriteAppsOverride = ''
+      [org.gnome.shell]
+      favorite-apps=[ 'firefox.desktop', 'org.gnome.Geary.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Photos.desktop', 'org.gnome.Nautilus.desktop' ]
+    '';
+    enable = true;
+  };
 
   services.xserver.displayManager = {
     gdm = {
diff --git a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
index 199e5f9206b..e49ceba2424 100644
--- a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
+++ b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
@@ -6,12 +6,7 @@
 let
   nodes = builtins.mapAttrs (vm: module: {
     _file = "${networkExpr}@node-${vm}";
-    imports = [
-      module
-      ({ pkgs, ... }: {
-        virtualisation.qemu.package = pkgs.qemu;
-      })
-    ];
+    imports = [ module ];
   }) (import networkExpr);
 in
 
@@ -20,4 +15,4 @@ with import ../../../../lib/testing-python.nix {
   pkgs = import ../../../../.. { inherit system config; };
 };
 
-(makeTest { inherit nodes; testScript = ""; }).driver
+(makeTest { inherit nodes; testScript = ""; }).driverInteractive
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index cce4e8e74b4..aa3b71a6124 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -173,6 +173,7 @@
   ./programs/wavemon.nix
   ./programs/waybar.nix
   ./programs/wireshark.nix
+  ./programs/wshowkeys.nix
   ./programs/x2goserver.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
diff --git a/nixos/modules/programs/wshowkeys.nix b/nixos/modules/programs/wshowkeys.nix
new file mode 100644
index 00000000000..09b008af1d5
--- /dev/null
+++ b/nixos/modules/programs/wshowkeys.nix
@@ -0,0 +1,22 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.wshowkeys;
+in {
+  meta.maintainers = with maintainers; [ primeos ];
+
+  options = {
+    programs.wshowkeys = {
+      enable = mkEnableOption ''
+        wshowkeys (displays keypresses on screen on supported Wayland
+        compositors). It requires root permissions to read input events, but
+        these permissions are dropped after startup'';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.wshowkeys.source = "${pkgs.wshowkeys}/bin/wshowkeys";
+  };
+}
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 5732620f290..47f6bead7c3 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -63,7 +63,7 @@ let
     script = with builtins; concatStringsSep "\n" (mapAttrsToList (cert: data: ''
       for fixpath in /var/lib/acme/${escapeShellArg cert} /var/lib/acme/.lego/${escapeShellArg cert}; do
         if [ -d "$fixpath" ]; then
-          chmod -R 750 "$fixpath"
+          chmod -R u=rwX,g=rX,o= "$fixpath"
           chown -R acme:${data.group} "$fixpath"
         fi
       done
@@ -271,7 +271,7 @@ let
 
         mv domainhash.txt certificates/
         chmod 640 certificates/*
-        chmod -R 700 accounts/*
+        chmod -R u=rwX,g=,o= accounts/*
 
         # Group might change between runs, re-apply it
         chown 'acme:${data.group}' certificates/*
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index a20d0a243a8..a517f9e51ce 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -318,6 +318,42 @@ let
         '';
       };
 
+      gnupg = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            If enabled, pam_gnupg will attempt to automatically unlock the
+            user's GPG keys with the login password via
+            <command>gpg-agent</command>. The keygrips of all keys to be
+            unlocked should be written to <filename>~/.pam-gnupg</filename>,
+            and can be queried with <command>gpg -K --with-keygrip</command>.
+            Presetting passphrases must be enabled by adding
+            <literal>allow-preset-passphrase</literal> in
+            <filename>~/.gnupg/gpg-agent.conf</filename>.
+          '';
+        };
+
+        noAutostart = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Don't start <command>gpg-agent</command> if it is not running.
+            Useful in conjunction with starting <command>gpg-agent</command> as
+            a systemd user service.
+          '';
+        };
+
+        storeOnly = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Don't send the password immediately after login, but store for PAM
+            <literal>session</literal>.
+          '';
+        };
+      };
+
       text = mkOption {
         type = types.nullOr types.lines;
         description = "Contents of the PAM service file.";
@@ -386,6 +422,7 @@ let
             || cfg.enableKwallet
             || cfg.enableGnomeKeyring
             || cfg.googleAuthenticator.enable
+            || cfg.gnupg.enable
             || cfg.duoSecurity.enable)) ''
               auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth
               ${optionalString config.security.pam.enableEcryptfs
@@ -397,6 +434,10 @@ let
                  " kwalletd=${pkgs.kdeFrameworks.kwallet.bin}/bin/kwalletd5")}
               ${optionalString cfg.enableGnomeKeyring
                 "auth optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so"}
+              ${optionalString cfg.gnupg.enable
+                "auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"
+                + optionalString cfg.gnupg.storeOnly " store-only"
+               }
               ${optionalString cfg.googleAuthenticator.enable
                 "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"}
               ${optionalString cfg.duoSecurity.enable
@@ -472,6 +513,10 @@ let
                " kwalletd=${pkgs.kdeFrameworks.kwallet.bin}/bin/kwalletd5")}
           ${optionalString (cfg.enableGnomeKeyring)
               "session optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"}
+          ${optionalString cfg.gnupg.enable
+              "session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"
+              + optionalString cfg.gnupg.noAutostart " no-autostart"
+           }
           ${optionalString (config.virtualisation.lxc.lxcfs.enable)
                "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"}
         '');
diff --git a/nixos/modules/services/backup/syncoid.nix b/nixos/modules/services/backup/syncoid.nix
index fff119c2cf0..e72e3fa59cf 100644
--- a/nixos/modules/services/backup/syncoid.nix
+++ b/nixos/modules/services/backup/syncoid.nix
@@ -4,6 +4,15 @@ with lib;
 
 let
   cfg = config.services.syncoid;
+
+  # Extract pool names of local datasets (ones that don't contain "@") that
+  # have the specified type (either "source" or "target")
+  getPools = type: unique (map (d: head (builtins.match "([^/]+).*" d)) (
+    # Filter local datasets
+    filter (d: !hasInfix "@" d)
+    # Get datasets of the specified type
+    (catAttrs type (attrValues cfg.commands))
+  ));
 in {
 
     # Interface
@@ -26,14 +35,25 @@ in {
 
       user = mkOption {
         type = types.str;
-        default = "root";
+        default = "syncoid";
         example = "backup";
         description = ''
-          The user for the service. Sudo or ZFS privilege delegation must be
-          configured to use a user other than root.
+          The user for the service. ZFS privilege delegation will be
+          automatically configured for any local pools used by syncoid if this
+          option is set to a user other than root. The user will be given the
+          "hold" and "send" privileges on any pool that has datasets being sent
+          and the "create", "mount", "receive", and "rollback" privileges on
+          any pool that has datasets being received.
         '';
       };
 
+      group = mkOption {
+        type = types.str;
+        default = "syncoid";
+        example = "backup";
+        description = "The group for the service.";
+      };
+
       sshKey = mkOption {
         type = types.nullOr types.path;
         # Prevent key from being copied to store
@@ -150,6 +170,18 @@ in {
     # Implementation
 
     config = mkIf cfg.enable {
+      users =  {
+        users = mkIf (cfg.user == "syncoid") {
+          syncoid = {
+            group = cfg.group;
+            isSystemUser = true;
+          };
+        };
+        groups = mkIf (cfg.group == "syncoid") {
+          syncoid = {};
+        };
+      };
+
       systemd.services.syncoid = {
         description = "Syncoid ZFS synchronization service";
         script = concatMapStringsSep "\n" (c: lib.escapeShellArgs
@@ -160,10 +192,22 @@ in {
             ++ c.extraArgs
             ++ [ "--sendoptions" c.sendOptions
                  "--recvoptions" c.recvOptions
+                 "--no-privilege-elevation"
                  c.source c.target
                ])) (attrValues cfg.commands);
         after = [ "zfs.target" ];
-        serviceConfig.User = cfg.user;
+        serviceConfig = {
+          ExecStartPre = (map (pool: lib.escapeShellArgs [
+            "+/run/booted-system/sw/bin/zfs" "allow"
+            cfg.user "hold,send" pool
+          ]) (getPools "source")) ++
+          (map (pool: lib.escapeShellArgs [
+            "+/run/booted-system/sw/bin/zfs" "allow"
+            cfg.user "create,mount,receive,rollback" pool
+          ]) (getPools "target"));
+          User = cfg.user;
+          Group = cfg.group;
+        };
         startAt = cfg.interval;
       };
     };
diff --git a/nixos/modules/services/desktops/flatpak.nix b/nixos/modules/services/desktops/flatpak.nix
index 7da92cc9f26..d0f6b66328a 100644
--- a/nixos/modules/services/desktops/flatpak.nix
+++ b/nixos/modules/services/desktops/flatpak.nix
@@ -15,6 +15,18 @@ in {
   options = {
     services.flatpak = {
       enable = mkEnableOption "flatpak";
+
+      guiPackages = mkOption {
+        internal = true;
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.gnome3.gnome-software ]";
+        description = ''
+          Packages that provide an interface for flatpak
+          (like gnome-software) that will be automatically available
+          to all users when flatpak is enabled.
+        '';
+      };
     };
   };
 
@@ -28,7 +40,7 @@ in {
       }
     ];
 
-    environment.systemPackages = [ pkgs.flatpak ];
+    environment.systemPackages = [ pkgs.flatpak ] ++ cfg.guiPackages;
 
     services.dbus.packages = [ pkgs.flatpak ];
 
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 2900c37f990..0b7d5575c11 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -86,7 +86,8 @@ in
 
     ipv6 = mkOption {
       type = types.bool;
-      default = false;
+      default = config.networking.enableIPv6;
+      defaultText = "config.networking.enableIPv6";
       description = "Whether to use IPv6.";
     };
 
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
index b9333cd19a2..ee7e9b0454d 100644
--- a/nixos/modules/services/networking/dnscrypt-wrapper.nix
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -55,7 +55,10 @@ let
   rotateKeys = ''
     # check if keys are not expired
     keyValid() {
-      fingerprint=$(dnscrypt-wrapper --show-provider-publickey | awk '{print $(NF)}')
+      fingerprint=$(dnscrypt-wrapper \
+        --show-provider-publickey \
+        --provider-publickey-file=${publicKey} \
+        | awk '{print $(NF)}')
       dnscrypt-proxy --test=${toString (cfg.keys.checkInterval + 1)} \
         --resolver-address=127.0.0.1:${toString cfg.port} \
         --provider-name=${cfg.providerName} \
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index d2feb93e2b7..4a85b3956da 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -123,12 +123,33 @@ in
               '';
             };
 
+            passwordFile = mkOption {
+              type = with types; uniq (nullOr str);
+              example = "/path/to/file";
+              default = null;
+              description = ''
+                Specifies the path to a file containing the
+                clear text password for the MQTT user.
+              '';
+            };
+
             hashedPassword = mkOption {
               type = with types; uniq (nullOr str);
               default = null;
               description = ''
                 Specifies the hashed password for the MQTT User.
-                <option>hashedPassword</option> overrides <option>password</option>.
+                To generate hashed password install <literal>mosquitto</literal>
+                package and use <literal>mosquitto_passwd</literal>.
+              '';
+            };
+
+            hashedPasswordFile = mkOption {
+              type = with types; uniq (nullOr str);
+              example = "/path/to/file";
+              default = null;
+              description = ''
+                Specifies the path to a file containing the
+                hashed password for the MQTT user.
                 To generate hashed password install <literal>mosquitto</literal>
                 package and use <literal>mosquitto_passwd</literal>.
               '';
@@ -190,6 +211,13 @@ in
 
   config = mkIf cfg.enable {
 
+    assertions = mapAttrsToList (name: cfg: {
+      assertion = length (filter (s: s != null) (with cfg; [
+        password passwordFile hashedPassword hashedPasswordFile
+      ])) <= 1;
+      message = "Cannot set more than one password option";
+    }) cfg.users;
+
     systemd.services.mosquitto = {
       description = "Mosquitto MQTT Broker Daemon";
       wantedBy = [ "multi-user.target" ];
@@ -210,7 +238,11 @@ in
         touch ${cfg.dataDir}/passwd
       '' + concatStringsSep "\n" (
         mapAttrsToList (n: c:
-          if c.hashedPassword != null then
+          if c.hashedPasswordFile != null then
+            "echo '${n}:'$(cat '${c.hashedPasswordFile}') >> ${cfg.dataDir}/passwd"
+          else if c.passwordFile != null then
+            "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} $(cat '${c.passwordFile}')"
+          else if c.hashedPassword != null then
             "echo '${n}:${c.hashedPassword}' >> ${cfg.dataDir}/passwd"
           else optionalString (c.password != null)
             "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} '${c.password}'"
diff --git a/nixos/modules/services/web-apps/engelsystem.nix b/nixos/modules/services/web-apps/engelsystem.nix
index 899582a2030..2e755ae9d52 100644
--- a/nixos/modules/services/web-apps/engelsystem.nix
+++ b/nixos/modules/services/web-apps/engelsystem.nix
@@ -10,7 +10,7 @@ in {
         default = false;
         example = true;
         description = ''
-          Whether to enable engelsystem, an online tool for coordinating helpers
+          Whether to enable engelsystem, an online tool for coordinating volunteers
           and shifts on large events.
         '';
         type = lib.types.bool;
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 6d2ddea927e..631e92fd6e9 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -86,7 +86,7 @@ let
       ''}
 
       ssl_protocols ${cfg.sslProtocols};
-      ssl_ciphers ${cfg.sslCiphers};
+      ${optionalString (cfg.sslCiphers != null) "ssl_ciphers ${cfg.sslCiphers};"}
       ${optionalString (cfg.sslDhparam != null) "ssl_dhparam ${cfg.sslDhparam};"}
 
       ${optionalString (cfg.recommendedTlsSettings) ''
@@ -487,7 +487,7 @@ in
       };
 
       sslCiphers = mkOption {
-        type = types.str;
+        type = types.nullOr types.str;
         # Keep in sync with https://ssl-config.mozilla.org/#server=nginx&config=intermediate
         default = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
         description = "Ciphers to choose from when negotiating TLS handshakes.";
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 97eb74991ab..9dfac56c7fe 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -17,6 +17,11 @@ let
     '';
   };
 
+  defaultFavoriteAppsOverride = ''
+    [org.gnome.shell]
+    favorite-apps=[ 'org.gnome.Geary.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Photos.desktop', 'org.gnome.Nautilus.desktop' ]
+  '';
+
   nixos-gsettings-desktop-schemas = let
     defaultPackages = with pkgs; [ gsettings-desktop-schemas gnome3.gnome-shell ];
   in
@@ -42,8 +47,7 @@ let
        [org.gnome.desktop.screensaver]
        picture-uri='file://${pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom.gnomeFilePath}'
 
-       [org.gnome.shell]
-       favorite-apps=[ 'org.gnome.Epiphany.desktop', 'org.gnome.Geary.desktop', 'org.gnome.Music.desktop', 'org.gnome.Photos.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]
+       ${cfg.favoriteAppsOverride}
 
        ${cfg.extraGSettingsOverrides}
      EOF
@@ -123,6 +127,17 @@ in
         apply = list: list ++ [ pkgs.gnome3.gnome-shell pkgs.gnome3.gnome-shell-extensions ];
       };
 
+      favoriteAppsOverride = mkOption {
+        internal = true; # this is messy
+        default = defaultFavoriteAppsOverride;
+        type = types.lines;
+        example = literalExample ''
+          [org.gnome.shell]
+          favorite-apps=[ 'firefox.desktop', 'org.gnome.Calendar.desktop' ]
+        '';
+        description = "List of desktop files to put as favorite apps into gnome-shell. These need to be installed somehow globally.";
+      };
+
       extraGSettingsOverrides = mkOption {
         default = "";
         type = types.lines;
@@ -215,6 +230,11 @@ in
 
        # If gnome3 is installed, build vim for gtk3 too.
       nixpkgs.config.vim.gui = "gtk3";
+
+      # Install gnome-software if flatpak is enabled
+      services.flatpak.guiPackages = [
+        pkgs.gnome3.gnome-software
+      ];
     })
 
     (mkIf flashbackEnabled {
@@ -397,7 +417,6 @@ in
         gnome-music
         gnome-photos
         gnome-screenshot
-        gnome-software
         gnome-system-monitor
         gnome-weather
         nautilus
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index e67e216f90d..cf02a71248b 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -180,7 +180,6 @@ in
         gtk3.out
         hicolor-icon-theme
         lightlocker
-        nixos-artwork.wallpapers.simple-dark-gray
         onboard
         qgnomeplatform
         shared-mime-info
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 2986bd4c4e3..be5fa88b8ad 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -45,15 +45,21 @@ with import ../../lib/qemu-flags.nix { inherit pkgs; };
     systemd.services."serial-getty@${qemuSerialDevice}".enable = false;
     systemd.services."serial-getty@hvc0".enable = false;
 
-    # Only use a serial console, no TTY.
-    # NOTE: optionalAttrs
-    #       test-instrumentation.nix appears to be used without qemu-vm.nix, so
-    #       we avoid defining consoles if not possible.
-    # TODO: refactor such that test-instrumentation can import qemu-vm
-    #       or declare virtualisation.qemu.console option in a module that's always imported
-    virtualisation.qemu = {
-      consoles = lib.optional (options ? virtualisation.qemu.consoles) qemuSerialDevice;
-      package  = lib.mkDefault pkgs.qemu_test;
+    # Only set these settings when the options exist. Some tests (e.g. those
+    # that do not specify any nodes, or an empty attr set as nodes) will not
+    # have the QEMU module loaded and thuse these options can't and should not
+    # be set.
+    virtualisation = lib.optionalAttrs (options ? virtualisation.qemu) {
+      qemu = {
+        # Only use a serial console, no TTY.
+        # NOTE: optionalAttrs
+        #       test-instrumentation.nix appears to be used without qemu-vm.nix, so
+        #       we avoid defining consoles if not possible.
+        # TODO: refactor such that test-instrumentation can import qemu-vm
+        #       or declare virtualisation.qemu.console option in a module that's always imported
+        consoles = [ qemuSerialDevice ];
+        package  = lib.mkDefault pkgs.qemu_test;
+      };
     };
 
     boot.initrd.preDeviceCommands =
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index 24de8cf1afb..4d9c391e046 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -329,5 +329,24 @@ let self = {
   "20.03".ap-east-1.hvm-ebs = "ami-0d18fdd309cdefa86";
   "20.03".sa-east-1.hvm-ebs = "ami-09859378158ae971d";
 
-  latest = self."20.03";
+  # 20.09.1465.9a0b14b097d
+  "20.09".eu-west-1.hvm-ebs = "ami-0d90f16418e3c364c";
+  "20.09".eu-west-2.hvm-ebs = "ami-0635ec0780ea57cfe";
+  "20.09".eu-west-3.hvm-ebs = "ami-0714e94352f2eabb9";
+  "20.09".eu-central-1.hvm-ebs = "ami-0979d39762a4d2a02";
+  "20.09".eu-north-1.hvm-ebs = "ami-0b14e273185c66e9b";
+  "20.09".us-east-1.hvm-ebs = "ami-0f8b063ac3f2d9645";
+  "20.09".us-east-2.hvm-ebs = "ami-0959202a0393fdd0c";
+  "20.09".us-west-1.hvm-ebs = "ami-096d50833b785478b";
+  "20.09".us-west-2.hvm-ebs = "ami-0fc31031df0df6104";
+  "20.09".ca-central-1.hvm-ebs = "ami-0787786a38cde3905";
+  "20.09".ap-southeast-1.hvm-ebs = "ami-0b3f693d3a2a0b9ae";
+  "20.09".ap-southeast-2.hvm-ebs = "ami-02471872bc876b610";
+  "20.09".ap-northeast-1.hvm-ebs = "ami-06505fd2bf44a59a7";
+  "20.09".ap-northeast-2.hvm-ebs = "ami-0754b4c014eea1e8a";
+  "20.09".ap-south-1.hvm-ebs = "ami-05100e32242ae65a6";
+  "20.09".ap-east-1.hvm-ebs = "ami-045288859a39de009";
+  "20.09".sa-east-1.hvm-ebs = "ami-0a937748db48fb00d";
+
+  latest = self."20.09";
 }; in self
diff --git a/nixos/tests/bitcoind.nix b/nixos/tests/bitcoind.nix
index 09f3e4a6ec0..9068b29b8e5 100644
--- a/nixos/tests/bitcoind.nix
+++ b/nixos/tests/bitcoind.nix
@@ -31,16 +31,16 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     machine.wait_for_unit("bitcoind-testnet.service")
 
     machine.wait_until_succeeds(
-        'curl --user rpc:rpc --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:8332 |  grep \'"chain":"main"\' '
+        'curl --fail --user rpc:rpc --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:8332 |  grep \'"chain":"main"\' '
     )
     machine.wait_until_succeeds(
-        'curl --user rpc2:rpc2 --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:8332 |  grep \'"chain":"main"\' '
+        'curl --fail --user rpc2:rpc2 --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:8332 |  grep \'"chain":"main"\' '
     )
     machine.wait_until_succeeds(
-        'curl --user rpc:rpc --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:18332 |  grep \'"chain":"test"\' '
+        'curl --fail --user rpc:rpc --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:18332 |  grep \'"chain":"test"\' '
     )
     machine.wait_until_succeeds(
-        'curl --user rpc2:rpc2 --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:18332 |  grep \'"chain":"test"\' '
+        'curl --fail --user rpc2:rpc2 --data-binary \'{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }\' -H \'content-type: text/plain;\' localhost:18332 |  grep \'"chain":"test"\' '
     )
   '';
 })
diff --git a/nixos/tests/caddy.nix b/nixos/tests/caddy.nix
index 445a7fa6b0b..f2de34ff2da 100644
--- a/nixos/tests/caddy.nix
+++ b/nixos/tests/caddy.nix
@@ -57,11 +57,13 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
     def check_etag(url):
         etag = webserver.succeed(
-            "curl -v '{}' 2>&1 | sed -n -e \"s/^< [Ee][Tt][Aa][Gg]: *//p\"".format(url)
+            "curl --fail -v '{}' 2>&1 | sed -n -e \"s/^< [Ee][Tt][Aa][Gg]: *//p\"".format(
+                url
+            )
         )
         etag = etag.replace("\r\n", " ")
         http_code = webserver.succeed(
-            "curl --silent --show-error -o /dev/null -w \"%{{http_code}}\" --head -H 'If-None-Match: {}' {}".format(
+            "curl --fail --silent --show-error -o /dev/null -w \"%{{http_code}}\" --head -H 'If-None-Match: {}' {}".format(
                 etag, url
             )
         )
diff --git a/nixos/tests/cadvisor.nix b/nixos/tests/cadvisor.nix
index 60c04f14780..664aa3ad876 100644
--- a/nixos/tests/cadvisor.nix
+++ b/nixos/tests/cadvisor.nix
@@ -19,16 +19,16 @@ import ./make-test-python.nix ({ pkgs, ... } : {
   testScript =  ''
       start_all()
       machine.wait_for_unit("cadvisor.service")
-      machine.succeed("curl http://localhost:8080/containers/")
+      machine.succeed("curl -f http://localhost:8080/containers/")
 
       influxdb.wait_for_unit("influxdb.service")
 
       # create influxdb database
       influxdb.succeed(
-          'curl -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE root"'
+          'curl -f -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE root"'
       )
 
       influxdb.wait_for_unit("cadvisor.service")
-      influxdb.succeed("curl http://localhost:8080/containers/")
+      influxdb.succeed("curl -f http://localhost:8080/containers/")
     '';
 })
diff --git a/nixos/tests/cfssl.nix b/nixos/tests/cfssl.nix
index e291fc285fb..170f09d9b76 100644
--- a/nixos/tests/cfssl.nix
+++ b/nixos/tests/cfssl.nix
@@ -38,7 +38,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
   testScript =
   let
     cfsslrequest = with pkgs; writeScript "cfsslrequest" ''
-      curl -X POST -H "Content-Type: application/json" -d @${csr} \
+      curl -f -X POST -H "Content-Type: application/json" -d @${csr} \
         http://localhost:8888/api/v1/cfssl/newkey | ${cfssl}/bin/cfssljson /tmp/certificate
     '';
     csr = pkgs.writeText "csr.json" (builtins.toJSON {
diff --git a/nixos/tests/common/acme/server/README.md b/nixos/tests/common/acme/server/README.md
new file mode 100644
index 00000000000..9de2b2c7102
--- /dev/null
+++ b/nixos/tests/common/acme/server/README.md
@@ -0,0 +1,21 @@
+# Fake Certificate Authority for ACME testing
+
+This will set up a test node running [pebble](https://github.com/letsencrypt/pebble)
+to serve ACME certificate requests.
+
+## "Snake oil" certs
+
+The snake oil certs are hard coded into the repo for reasons explained [here](https://github.com/NixOS/nixpkgs/pull/91121#discussion_r505410235).
+The root of the issue is that Nix will hash the derivation based on the arguments
+to mkDerivation, not the output. [Minica](https://github.com/jsha/minica) will
+always generate a random certificate even if the arguments are unchanged. As a
+result, it's possible to end up in a situation where the cached and local
+generated certs mismatch and cause issues with testing.
+
+To generate new certificates, run the following commands:
+
+```bash
+nix-build generate-certs.nix
+cp result/* .
+rm result
+```
diff --git a/nixos/tests/common/acme/server/acme.test.cert.pem b/nixos/tests/common/acme/server/acme.test.cert.pem
new file mode 100644
index 00000000000..76b0d916a81
--- /dev/null
+++ b/nixos/tests/common/acme/server/acme.test.cert.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDLDCCAhSgAwIBAgIIRDAN3FHH//IwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
+AxMVbWluaWNhIHJvb3QgY2EgNzg3NDZmMB4XDTIwMTAyMTEzMjgzNloXDTIyMTEy
+MDEzMjgzNlowFDESMBAGA1UEAxMJYWNtZS50ZXN0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAo8XjMVUaljcaqQ5MFhfPuQgSwdyXEUbpSHz+5yPkE0h9
+Z4Xu5BJF1Oq7h5ggCtadVsIspiY6Jm6aWDOjlh4myzW5UNBNUG3OPEk50vmmHFeH
+pImHO/d8yb33QoF9VRcTZs4tuJYg7l9bSs4jNG72vYvv2YiGAcmjJcsmAZIfniCN
+Xf/LjIm+Cxykn+Vo3UuzO1w5/iuofdgWO/aZxMezmXUivlL3ih4cNzCJei8WlB/l
+EnHrkcy3ogRmmynP5zcz7vmGIJX2ji6dhCa4Got5B7eZK76o2QglhQXqPatG0AOY
+H+RfQfzKemqPG5om9MgJtwFtTOU1LoaiBw//jXKESQIDAQABo3YwdDAOBgNVHQ8B
+Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB
+/wQCMAAwHwYDVR0jBBgwFoAU+8IZlLV/Qp5CXqpXMLvtxWlxcJwwFAYDVR0RBA0w
+C4IJYWNtZS50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQB0pe8I5/VDkB5VMgQB2GJV
+GKzyigfWbVez9uLmqMj9PPP/zzYKSYeq+91aMuOZrnH7NqBxSTwanULkmqAmhbJJ
+YkXw+FlFekf9FyxcuArzwzzNZDSGcjcdXpN8S2K1qkBd00iSJF9kU7pdZYCIKR20
+QirdBrELEfsJ3GU62a6N3a2YsrisZUvq5TbjGJDcytAtt+WG3gmV7RInLdFfPwbw
+bEHPCnx0uiV0nxLjd/aVT+RceVrFQVt4hR99jLoMlBitSKluZ1ljsrpIyroBhQT0
+pp/pVi6HJdijG0fsPrC325NEGAwcpotLUhczoeM/rffKJd54wLhDkfYxOyRZXivs
+-----END CERTIFICATE-----
diff --git a/nixos/tests/common/acme/server/acme.test.key.pem b/nixos/tests/common/acme/server/acme.test.key.pem
new file mode 100644
index 00000000000..741df99a372
--- /dev/null
+++ b/nixos/tests/common/acme/server/acme.test.key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAo8XjMVUaljcaqQ5MFhfPuQgSwdyXEUbpSHz+5yPkE0h9Z4Xu
+5BJF1Oq7h5ggCtadVsIspiY6Jm6aWDOjlh4myzW5UNBNUG3OPEk50vmmHFeHpImH
+O/d8yb33QoF9VRcTZs4tuJYg7l9bSs4jNG72vYvv2YiGAcmjJcsmAZIfniCNXf/L
+jIm+Cxykn+Vo3UuzO1w5/iuofdgWO/aZxMezmXUivlL3ih4cNzCJei8WlB/lEnHr
+kcy3ogRmmynP5zcz7vmGIJX2ji6dhCa4Got5B7eZK76o2QglhQXqPatG0AOYH+Rf
+QfzKemqPG5om9MgJtwFtTOU1LoaiBw//jXKESQIDAQABAoIBADox/2FwVFo8ioS4
+R+Ex5OZjMAcjU6sX/516jTmlT05q2+UFerYgqB/YqXqtW/V9/brulN8VhmRRuRbO
+grq9TBu5o3hMDK0f18EkZB/MBnLbx594H033y6gEkPBZAyhRYtuNOEH3VwxdZhtW
+1Lu1EoiYSUqLcNMBy6+KWJ8GRaXyacMYBlj2lMHmyzkA/t1+2mwTGC3lT6zN0F5Y
+E5umXOxsn6Tb6q3KM9O5IvtmMMKpgj4HIHZLZ6j40nNgHwGRaAv4Sha/vx0DeBw3
+6VlNiTTPdShEkhESlM5/ocqTfI92VHJpM5gkqTYOWBi2aKIPfAopXoqoJdWl4pQ/
+NCFIu2ECgYEAzntNKIcQtf0ewe0/POo07SIFirvz6jVtYNMTzeQfL6CoEjYArJeu
+Vzc4wEQfA4ZFVerBb1/O6M449gI3zex1PH4AX0h8q8DSjrppK1Jt2TnpVh97k7Gg
+Tnat/M/yW3lWYkcMVJJ3AYurXLFTT1dYP0HvBwZN04yInrEcPNXKfmcCgYEAywyJ
+51d4AE94PrANathKqSI/gk8sP+L1gzylZCcUEAiGk/1r45iYB4HN2gvWbS+CvSdp
+F7ShlDWrTaNh2Bm1dgTjc4pWb4J+CPy/KN2sgLwIuM4+ZWIZmEDcio6khrM/gNqK
+aR7xUsvWsqU26O84woY/xR8IHjSNF7cFWE1H2c8CgYEAt6SSi2kVQ8dMg84uYE8t
+o3qO00U3OycpkOQqyQQLeKC62veMwfRl6swCfX4Y11mkcTXJtPTRYd2Ia8StPUkB
+PDwUuKoPt/JXUvoYb59wc7M+BIsbrdBdc2u6cw+/zfutCNuH6/AYSBeg4WAVaIuW
+wSwzG1xP+8cR+5IqOzEqWCECgYATweeVTCyQEyuHJghYMi2poXx+iIesu7/aAkex
+pB/Oo5W8xrb90XZRnK7UHbzCqRHWqAQQ23Gxgztk9ZXqui2vCzC6qGZauV7cLwPG
+zTMg36sVmHP314DYEM+k59ZYiQ6P0jQPoIQo407D2VGrfsOOIhQIcUmP7tsfyJ5L
+hlGMfwKBgGq4VNnnuX8I5kl03NpaKfG+M8jEHmVwtI9RkPTCCX9bMjeG0cDxqPTF
+TRkf3r8UWQTZ5QfAfAXYAOlZvmGhHjSembRbXMrMdi3rGsYRSrQL6n5NHnORUaMy
+FCWo4gyAnniry7tx9dVNgmHmbjEHuQnf8AC1r3dibRCjvJWUiQ8H
+-----END RSA PRIVATE KEY-----
diff --git a/nixos/tests/common/acme/server/ca.cert.pem b/nixos/tests/common/acme/server/ca.cert.pem
new file mode 100644
index 00000000000..5c33e879b67
--- /dev/null
+++ b/nixos/tests/common/acme/server/ca.cert.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSzCCAjOgAwIBAgIIeHRvRrNvbGQwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
+AxMVbWluaWNhIHJvb3QgY2EgNzg3NDZmMCAXDTIwMTAyMTEzMjgzNloYDzIxMjAx
+MDIxMTMyODM2WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA3ODc0NmYwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrNTzVLDJOKtGYGLU98EEcLKps
+tXHCLC6G54LKbEcU80fn+ArX8qsPSHyhdXQkcYjq6Vh/EDJ1TctyRSnvAjwyG4Aa
+1Zy1QFc/JnjMjvzimCkUc9lQ+wkLwHSM/KGwR1cGjmtQ/EMClZTA0NwulJsXMKVz
+bd5asXbq/yJTQ5Ww25HtdNjwRQXTvB7r3IKcY+DsED9CvFvC9oG/ZhtZqZuyyRdC
+kFUrrv8WNUDkWSN+lMR6xMx8v0583IN6f11IhX0b+svK98G81B2eswBdkzvVyv9M
+unZBO0JuJG8sdM502KhWLmzBC1ZbvgUBF9BumDRpMFH4DCj7+qQ2taWeGyc7AgMB
+AAGjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
+BgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBT7whmUtX9CnkJe
+qlcwu+3FaXFwnDAfBgNVHSMEGDAWgBT7whmUtX9CnkJeqlcwu+3FaXFwnDANBgkq
+hkiG9w0BAQsFAAOCAQEARMe1wKmF33GjEoLLw0oDDS4EdAv26BzCwtrlljsEtwQN
+95oSzUNd6o4Js7WCG2o543OX6cxzM+yju8TES3+vJKDgsbNMU0bWCv//tdrb0/G8
+OkU3Kfi5q4fOauZ1pqGv/pXdfYhZ5ieB/zwis3ykANe5JfB0XqwCb1Vd0C3UCIS2
+NPKngRwNSzphIsbzfvxGDkdM1enuGl5CVyDhrwTMqGaJGDSOv6U5jKFxKRvigqTN
+Ls9lPmT5NXYETduWLBR3yUIdH6kZXrcozZ02B9vjOB2Cv4RMDc+9eM30CLIWpf1I
+097e7JkhzxFhfC/bMMt3P1FeQc+fwH91wdBmNi7tQw==
+-----END CERTIFICATE-----
diff --git a/nixos/tests/common/acme/server/ca.key.pem b/nixos/tests/common/acme/server/ca.key.pem
new file mode 100644
index 00000000000..ed46f5dccf4
--- /dev/null
+++ b/nixos/tests/common/acme/server/ca.key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAqzU81SwyTirRmBi1PfBBHCyqbLVxwiwuhueCymxHFPNH5/gK
+1/KrD0h8oXV0JHGI6ulYfxAydU3LckUp7wI8MhuAGtWctUBXPyZ4zI784pgpFHPZ
+UPsJC8B0jPyhsEdXBo5rUPxDApWUwNDcLpSbFzClc23eWrF26v8iU0OVsNuR7XTY
+8EUF07we69yCnGPg7BA/QrxbwvaBv2YbWambsskXQpBVK67/FjVA5FkjfpTEesTM
+fL9OfNyDen9dSIV9G/rLyvfBvNQdnrMAXZM71cr/TLp2QTtCbiRvLHTOdNioVi5s
+wQtWW74FARfQbpg0aTBR+Awo+/qkNrWlnhsnOwIDAQABAoIBAA3ykVkgd5ysmlSU
+trcsCnHcJaojgff6l3PACoSpG4VWaGY6a8+54julgRm6MtMBONFCX0ZCsImj484U
+Wl0xRmwil2YYPuL5MeJgJPktMObY1IfpBCw3tz3w2M3fiuCMf0d2dMGtO1xLiUnH
++hgFXTkfamsj6ThkOrbcQBSebeRxbKM5hqyCaQoieV+0IJnyxUVq/apib8N50VsH
+SHd4oqLUuEZgg6N70+l5DpzedJUb4nrwS/KhUHUBgnoPItYBCiGPmrwLk7fUhPs6
+kTDqJDtc/xW/JbjmzhWEpVvtumcC/OEKULss7HLdeQqwVBrRQkznb0M9AnSra3d0
+X11/Y4ECgYEA3FC8SquLPFb2lHK4+YbJ4Ac6QVWeYFEHiZ0Rj+CmONmjcAvOGLPE
+SblRLm3Nbrkxbm8FF6/AfXa/rviAKEVPs5xqGfSDw/3n1uInPcmShiBCLwM/jHH5
+NeVG+R5mTg5zyQ/pQMLWRcs+Ail+ZAnZuoGpW3Cdc8OtCUYFQ7XB6nsCgYEAxvBJ
+zFxcTtsDzWbMWXejugQiUqJcEbKWwEfkRbf3J2rAVO2+EFr7LxdRfN2VwPiTQcWc
+LnN2QN+ouOjqBMTh3qm5oQY+TLLHy86k9g1k0gXWkMRQgP2ZdfWH1HyrwjLUgLe1
+VezFN7N1azgy6xFkInAAvuA4loxElZNvkGBgekECgYA/Xw26ILvNIGqO6qzgQXAh
++5I7JsiGheg4IjDiBMlrQtbrLMoceuD0H9UFGNplhel9DXwWgxxIOncKejpK2x0A
+2fX+/0FDh+4+9hA5ipiV8gN3iGSoHkSDxy5yC9d7jlapt+TtFt4Rd1OfxZWwatDw
+/8jaH3t6yAcmyrhK8KYVrwKBgAE5KwsBqmOlvyE9N5Z5QN189wUREIXfVkP6bTHs
+jq2EX4hmKdwJ4y+H8i1VY31bSfSGlY5HkXuWpH/2lrHO0CDBZG3UDwADvWzIaYVF
+0c/kz0v2mRQh+xaZmus4lQnNrDbaalgL666LAPbW0qFVaws3KxoBYPe0BxvwWyhF
+H3LBAoGBAKRRNsq2pWQ8Gqxc0rVoH0FlexU9U2ci3lsLmgEB0A/o/kQkSyAxaRM+
+VdKp3sWfO8o8lX5CVQslCNBSjDTNcat3Co4NEBLg6Xv1yKN/WN1GhusnchP9szsP
+oU47gC89QhUyWSd6vvr2z2NG9C3cACxe4dhDSHQcE4nHSldzCKv2
+-----END RSA PRIVATE KEY-----
diff --git a/nixos/tests/common/acme/server/default.nix b/nixos/tests/common/acme/server/default.nix
index cea10c16900..1c3bfdf76b7 100644
--- a/nixos/tests/common/acme/server/default.nix
+++ b/nixos/tests/common/acme/server/default.nix
@@ -51,10 +51,7 @@
 # that it has to be started _before_ the ACME service.
 { config, pkgs, lib, ... }:
 let
-  testCerts = import ./snakeoil-certs.nix {
-    minica = pkgs.minica;
-    mkDerivation = pkgs.stdenv.mkDerivation;
-  };
+  testCerts = import ./snakeoil-certs.nix;
   domain = testCerts.domain;
 
   resolver = let
diff --git a/nixos/tests/common/acme/server/generate-certs.nix b/nixos/tests/common/acme/server/generate-certs.nix
new file mode 100644
index 00000000000..cd8fe0dffca
--- /dev/null
+++ b/nixos/tests/common/acme/server/generate-certs.nix
@@ -0,0 +1,29 @@
+# Minica can provide a CA key and cert, plus a key
+# and cert for our fake CA server's Web Front End (WFE).
+{
+  pkgs ? import <nixpkgs> {},
+  minica ? pkgs.minica,
+  mkDerivation ? pkgs.stdenv.mkDerivation
+}:
+let
+  conf = import ./snakeoil-certs.nix;
+  domain = conf.domain;
+in mkDerivation {
+  name = "test-certs";
+  buildInputs = [ minica ];
+  phases = [ "buildPhase" "installPhase" ];
+
+  buildPhase = ''
+    minica \
+      --ca-key ca.key.pem \
+      --ca-cert ca.cert.pem \
+      --domains ${domain}
+  '';
+
+  installPhase = ''
+    mkdir -p $out
+    mv ca.*.pem $out/
+    mv ${domain}/key.pem $out/${domain}.key.pem
+    mv ${domain}/cert.pem $out/${domain}.cert.pem
+  '';
+}
diff --git a/nixos/tests/common/acme/server/snakeoil-certs.nix b/nixos/tests/common/acme/server/snakeoil-certs.nix
index 4b6a38b8fa3..11c3f7fc929 100644
--- a/nixos/tests/common/acme/server/snakeoil-certs.nix
+++ b/nixos/tests/common/acme/server/snakeoil-certs.nix
@@ -1,37 +1,13 @@
-# Minica can provide a CA key and cert, plus a key
-# and cert for our fake CA server's Web Front End (WFE).
-{ minica, mkDerivation }:
 let
   domain = "acme.test";
-
-  selfSignedCertData = mkDerivation {
-    name = "test-certs";
-    buildInputs = [ minica ];
-    phases = [ "buildPhase" "installPhase" ];
-
-    buildPhase = ''
-      mkdir ca
-      minica \
-        --ca-key ca/key.pem \
-        --ca-cert ca/cert.pem \
-        --domains ${domain}
-      chmod 600 ca/*
-      chmod 640 ${domain}/*.pem
-    '';
-
-    installPhase = ''
-      mkdir -p $out
-      mv ${domain} ca $out/
-    '';
-  };
 in {
   inherit domain;
   ca = {
-    cert = "${selfSignedCertData}/ca/cert.pem";
-    key = "${selfSignedCertData}/ca/key.pem";
+    cert = ./ca.cert.pem;
+    key = ./ca.key.pem;
   };
   "${domain}" = {
-    cert = "${selfSignedCertData}/${domain}/cert.pem";
-    key = "${selfSignedCertData}/${domain}/key.pem";
+    cert = ./. + "/${domain}.cert.pem";
+    key = ./. + "/${domain}.key.pem";
   };
 }
diff --git a/nixos/tests/convos.nix b/nixos/tests/convos.nix
index b4ff1188fd8..af2758c857d 100644
--- a/nixos/tests/convos.nix
+++ b/nixos/tests/convos.nix
@@ -25,6 +25,6 @@ in
     machine.wait_for_unit("convos")
     machine.wait_for_open_port("${toString port}")
     machine.succeed("journalctl -u convos | grep -q 'Listening at.*${toString port}'")
-    machine.succeed("curl http://localhost:${toString port}/")
+    machine.succeed("curl -f http://localhost:${toString port}/")
   '';
 })
diff --git a/nixos/tests/corerad.nix b/nixos/tests/corerad.nix
index 37a1e90477a..638010f92f4 100644
--- a/nixos/tests/corerad.nix
+++ b/nixos/tests/corerad.nix
@@ -80,7 +80,7 @@ import ./make-test-python.nix (
           ), "SLAAC temporary address was not configured on client after router advertisement"
 
       with subtest("Verify HTTP debug server is configured"):
-          out = router.succeed("curl localhost:9430/metrics")
+          out = router.succeed("curl -f localhost:9430/metrics")
 
           assert (
               "corerad_build_info" in out
diff --git a/nixos/tests/docker-edge.nix b/nixos/tests/docker-edge.nix
index 96de885a554..703179eef19 100644
--- a/nixos/tests/docker-edge.nix
+++ b/nixos/tests/docker-edge.nix
@@ -43,7 +43,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     docker.fail("sudo -u noprivs docker ps")
     docker.succeed("docker stop sleeping")
 
-    # Must match version twice to ensure client and server versions are correct
-    docker.succeed('[ $(docker version | grep ${pkgs.docker-edge.version} | wc -l) = "2" ]')
+    # Must match version 4 times to ensure client and server git commits and versions are correct
+    docker.succeed('[ $(docker version | grep ${pkgs.docker-edge.version} | wc -l) = "4" ]')
   '';
 })
diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix
index edb9aec62db..c1c41b0fc11 100644
--- a/nixos/tests/docker-tools.nix
+++ b/nixos/tests/docker-tools.nix
@@ -115,7 +115,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
             "docker load --input='${examples.nginx}'",
             "docker run --name nginx -d -p 8000:80 ${examples.nginx.imageName}",
         )
-        docker.wait_until_succeeds("curl http://localhost:8000/")
+        docker.wait_until_succeeds("curl -f http://localhost:8000/")
         docker.succeed(
             "docker rm --force nginx", "docker rmi '${examples.nginx.imageName}'",
         )
diff --git a/nixos/tests/go-neb.nix b/nixos/tests/go-neb.nix
index d9e5db0b4a5..531ab5a6671 100644
--- a/nixos/tests/go-neb.nix
+++ b/nixos/tests/go-neb.nix
@@ -34,7 +34,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
     start_all()
     server.wait_for_unit("go-neb.service")
     server.wait_until_succeeds(
-        "curl -L http://localhost:4050/services/hooks/d2lraXBlZGlhX3NlcnZpY2U"
+        "curl -fL http://localhost:4050/services/hooks/d2lraXBlZGlhX3NlcnZpY2U"
     )
     server.wait_until_succeeds(
         "journalctl -eu go-neb -o cat | grep -q service_id=wikipedia_service"
diff --git a/nixos/tests/hadoop/hdfs.nix b/nixos/tests/hadoop/hdfs.nix
index 85aaab34b15..f1f98ed42eb 100644
--- a/nixos/tests/hadoop/hdfs.nix
+++ b/nixos/tests/hadoop/hdfs.nix
@@ -48,7 +48,7 @@ import ../make-test-python.nix ({...}: {
     datanode.wait_for_open_port(9866)
     datanode.wait_for_open_port(9867)
 
-    namenode.succeed("curl http://namenode:9870")
-    datanode.succeed("curl http://datanode:9864")
+    namenode.succeed("curl -f http://namenode:9870")
+    datanode.succeed("curl -f http://datanode:9864")
   '';
 })
diff --git a/nixos/tests/hadoop/yarn.nix b/nixos/tests/hadoop/yarn.nix
index 2264ecaff15..01077245d39 100644
--- a/nixos/tests/hadoop/yarn.nix
+++ b/nixos/tests/hadoop/yarn.nix
@@ -40,7 +40,7 @@ import ../make-test-python.nix ({...}: {
     nodemanager.wait_for_open_port(8042)
     nodemanager.wait_for_open_port(8041)
 
-    resourcemanager.succeed("curl http://localhost:8088")
-    nodemanager.succeed("curl http://localhost:8042")
+    resourcemanager.succeed("curl -f http://localhost:8088")
+    nodemanager.succeed("curl -f http://localhost:8042")
   '';
 })
diff --git a/nixos/tests/haproxy.nix b/nixos/tests/haproxy.nix
index ffb77c052a2..2c3878131b6 100644
--- a/nixos/tests/haproxy.nix
+++ b/nixos/tests/haproxy.nix
@@ -39,9 +39,9 @@ import ./make-test-python.nix ({ pkgs, ...}: {
     machine.wait_for_unit("multi-user.target")
     machine.wait_for_unit("haproxy.service")
     machine.wait_for_unit("httpd.service")
-    assert "We are all good!" in machine.succeed("curl -k http://localhost:80/index.txt")
+    assert "We are all good!" in machine.succeed("curl -fk http://localhost:80/index.txt")
     assert "haproxy_process_pool_allocated_bytes" in machine.succeed(
-        "curl -k http://localhost:80/metrics"
+        "curl -fk http://localhost:80/metrics"
     )
 
     with subtest("reload"):
@@ -49,7 +49,7 @@ import ./make-test-python.nix ({ pkgs, ...}: {
         # wait some time to ensure the following request hits the reloaded haproxy
         machine.sleep(5)
         assert "We are all good!" in machine.succeed(
-            "curl -k http://localhost:80/index.txt"
+            "curl -fk http://localhost:80/index.txt"
         )
   '';
 })
diff --git a/nixos/tests/hitch/default.nix b/nixos/tests/hitch/default.nix
index 904d12619d7..8a2193e75f2 100644
--- a/nixos/tests/hitch/default.nix
+++ b/nixos/tests/hitch/default.nix
@@ -28,6 +28,6 @@ import ../make-test-python.nix ({ pkgs, ... }:
       machine.wait_for_unit("multi-user.target")
       machine.wait_for_unit("hitch.service")
       machine.wait_for_open_port(443)
-      assert "We are all good!" in machine.succeed("curl -k https://localhost:443/index.txt")
+      assert "We are all good!" in machine.succeed("curl -fk https://localhost:443/index.txt")
     '';
 })
diff --git a/nixos/tests/hound.nix b/nixos/tests/hound.nix
index 27c65abdf27..b8b10022bd9 100644
--- a/nixos/tests/hound.nix
+++ b/nixos/tests/hound.nix
@@ -53,7 +53,7 @@ import ./make-test-python.nix ({ pkgs, ... } : {
     machine.wait_for_unit("hound.service")
     machine.wait_for_open_port(6080)
     machine.wait_until_succeeds(
-        "curl http://127.0.0.1:6080/api/v1/search\?stats\=fosho\&repos\=\*\&rng=%3A20\&q\=hi\&files\=\&i=nope | grep 'Filename' | grep 'hello'"
+        "curl -f http://127.0.0.1:6080/api/v1/search\?stats\=fosho\&repos\=\*\&rng=%3A20\&q\=hi\&files\=\&i=nope | grep 'Filename' | grep 'hello'"
     )
   '';
 })
diff --git a/nixos/tests/leaps.nix b/nixos/tests/leaps.nix
index ac0c602d445..ec5b69a7629 100644
--- a/nixos/tests/leaps.nix
+++ b/nixos/tests/leaps.nix
@@ -26,7 +26,7 @@ import ./make-test-python.nix ({ pkgs,  ... }:
       server.wait_for_open_port(6666)
       client.wait_for_unit("network.target")
       assert "leaps" in client.succeed(
-          "${pkgs.curl}/bin/curl http://server:6666/leaps/"
+          "${pkgs.curl}/bin/curl -f http://server:6666/leaps/"
       )
     '';
 })
diff --git a/nixos/tests/limesurvey.nix b/nixos/tests/limesurvey.nix
index 7228fcb8331..dad807fb733 100644
--- a/nixos/tests/limesurvey.nix
+++ b/nixos/tests/limesurvey.nix
@@ -20,7 +20,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
     machine.wait_for_unit("phpfpm-limesurvey.service")
     assert "The following surveys are available" in machine.succeed(
-        "curl http://example.local/"
+        "curl -f http://example.local/"
     )
   '';
 })
diff --git a/nixos/tests/mailcatcher.nix b/nixos/tests/mailcatcher.nix
index 2ef38544fe0..a55fba8a995 100644
--- a/nixos/tests/mailcatcher.nix
+++ b/nixos/tests/mailcatcher.nix
@@ -24,7 +24,7 @@ import ./make-test-python.nix ({ lib, ... }:
         'echo "this is the body of the email" | mail -s "subject" root@example.org'
     )
     assert "this is the body of the email" in machine.succeed(
-        "curl http://localhost:1080/messages/1.source"
+        "curl -f http://localhost:1080/messages/1.source"
     )
   '';
 })
diff --git a/nixos/tests/matrix-synapse.nix b/nixos/tests/matrix-synapse.nix
index 9ca80872176..6c8f1e188d5 100644
--- a/nixos/tests/matrix-synapse.nix
+++ b/nixos/tests/matrix-synapse.nix
@@ -77,12 +77,12 @@ in {
     start_all()
     serverpostgres.wait_for_unit("matrix-synapse.service")
     serverpostgres.wait_until_succeeds(
-        "curl -L --cacert ${ca_pem} https://localhost:8448/"
+        "curl --fail -L --cacert ${ca_pem} https://localhost:8448/"
     )
     serverpostgres.require_unit_state("postgresql.service")
     serversqlite.wait_for_unit("matrix-synapse.service")
     serversqlite.wait_until_succeeds(
-        "curl -L --cacert ${ca_pem} https://localhost:8448/"
+        "curl --fail -L --cacert ${ca_pem} https://localhost:8448/"
     )
     serversqlite.succeed("[ -e /var/lib/matrix-synapse/homeserver.db ]")
   '';
diff --git a/nixos/tests/mediawiki.nix b/nixos/tests/mediawiki.nix
index 008682310cf..702fefefa16 100644
--- a/nixos/tests/mediawiki.nix
+++ b/nixos/tests/mediawiki.nix
@@ -22,7 +22,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
 
     machine.wait_for_unit("phpfpm-mediawiki.service")
 
-    page = machine.succeed("curl -L http://localhost/")
+    page = machine.succeed("curl -fL http://localhost/")
     assert "MediaWiki has been installed" in page
   '';
 })
diff --git a/nixos/tests/metabase.nix b/nixos/tests/metabase.nix
index 1450a4e9086..65619cc793a 100644
--- a/nixos/tests/metabase.nix
+++ b/nixos/tests/metabase.nix
@@ -15,6 +15,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     start_all()
     machine.wait_for_unit("metabase.service")
     machine.wait_for_open_port(3000)
-    machine.wait_until_succeeds("curl -L http://localhost:3000/setup | grep Metabase")
+    machine.wait_until_succeeds("curl -fL http://localhost:3000/setup | grep Metabase")
   '';
 })
diff --git a/nixos/tests/morty.nix b/nixos/tests/morty.nix
index 64c5a27665d..ff30b7c072b 100644
--- a/nixos/tests/morty.nix
+++ b/nixos/tests/morty.nix
@@ -24,7 +24,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
     ''
       mortyProxyWithKey.wait_for_unit("default.target")
       mortyProxyWithKey.wait_for_open_port(3001)
-      mortyProxyWithKey.succeed("curl -L 127.0.0.1:3001 | grep MortyProxy")
+      mortyProxyWithKey.succeed("curl -fL 127.0.0.1:3001 | grep MortyProxy")
     '';
 
 })
diff --git a/nixos/tests/neo4j.nix b/nixos/tests/neo4j.nix
index 32ee7f501b8..8329e5630d7 100644
--- a/nixos/tests/neo4j.nix
+++ b/nixos/tests/neo4j.nix
@@ -15,6 +15,6 @@ import ./make-test-python.nix {
 
     master.wait_for_unit("neo4j")
     master.wait_for_open_port(7474)
-    master.succeed("curl http://localhost:7474/")
+    master.succeed("curl -f http://localhost:7474/")
   '';
 }
diff --git a/nixos/tests/nzbget.nix b/nixos/tests/nzbget.nix
index 12d8ed6ea8d..b39c9b035e6 100644
--- a/nixos/tests/nzbget.nix
+++ b/nixos/tests/nzbget.nix
@@ -21,7 +21,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     server.wait_for_unit("network.target")
     server.wait_for_open_port(6789)
     assert "This file is part of nzbget" in server.succeed(
-        "curl -s -u nzbget:tegbzn6789 http://127.0.0.1:6789"
+        "curl -f -s -u nzbget:tegbzn6789 http://127.0.0.1:6789"
     )
     server.succeed(
         "${pkgs.nzbget}/bin/nzbget -n -o Control_iP=127.0.0.1 -o Control_port=6789 -o Control_password=tegbzn6789 -V"
diff --git a/nixos/tests/oci-containers.nix b/nixos/tests/oci-containers.nix
index bb6c019f07c..0dfc7ffb276 100644
--- a/nixos/tests/oci-containers.nix
+++ b/nixos/tests/oci-containers.nix
@@ -32,7 +32,7 @@ let
       start_all()
       ${backend}.wait_for_unit("${backend}-nginx.service")
       ${backend}.wait_for_open_port(8181)
-      ${backend}.wait_until_succeeds("curl http://localhost:8181 | grep Hello")
+      ${backend}.wait_until_succeeds("curl -f http://localhost:8181 | grep Hello")
     '';
   };
 
diff --git a/nixos/tests/osrm-backend.nix b/nixos/tests/osrm-backend.nix
index db67a5a589f..4067d5b1a23 100644
--- a/nixos/tests/osrm-backend.nix
+++ b/nixos/tests/osrm-backend.nix
@@ -48,10 +48,10 @@ in {
     machine.wait_for_unit("osrm.service")
     machine.wait_for_open_port(${toString port})
     assert "Boulevard Rainier III" in machine.succeed(
-        "curl --silent '${query}' | jq .waypoints[0].name"
+        "curl --fail --silent '${query}' | jq .waypoints[0].name"
     )
     assert "Avenue de la Costa" in machine.succeed(
-        "curl --silent '${query}' | jq .waypoints[1].name"
+        "curl --fail --silent '${query}' | jq .waypoints[1].name"
     )
   '';
 })
diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix
index 355e7041d3f..fb83e6f976d 100644
--- a/nixos/tests/paperless.nix
+++ b/nixos/tests/paperless.nix
@@ -23,14 +23,14 @@ import ./make-test-python.nix ({ lib, ... } : {
     with subtest("Service gets ready"):
         machine.wait_for_unit("paperless-server.service")
         # Wait until server accepts connections
-        machine.wait_until_succeeds("curl -s localhost:28981")
+        machine.wait_until_succeeds("curl -fs localhost:28981")
 
     with subtest("Test document is consumed"):
         machine.wait_until_succeeds(
-            "(($(curl -s localhost:28981/api/documents/ | jq .count) == 1))"
+            "(($(curl -fs localhost:28981/api/documents/ | jq .count) == 1))"
         )
         assert "2005-10-16" in machine.succeed(
-            "curl -s localhost:28981/api/documents/ | jq '.results | .[0] | .created'"
+            "curl -fs localhost:28981/api/documents/ | jq '.results | .[0] | .created'"
         )
   '';
 })
diff --git a/nixos/tests/peerflix.nix b/nixos/tests/peerflix.nix
index 37628604d49..6e534dedc47 100644
--- a/nixos/tests/peerflix.nix
+++ b/nixos/tests/peerflix.nix
@@ -18,6 +18,6 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     start_all()
 
     peerflix.wait_for_unit("peerflix.service")
-    peerflix.wait_until_succeeds("curl localhost:9000")
+    peerflix.wait_until_succeeds("curl -f localhost:9000")
   '';
 })
diff --git a/nixos/tests/php/fpm.nix b/nixos/tests/php/fpm.nix
index 513abd94373..9ad515ebdde 100644
--- a/nixos/tests/php/fpm.nix
+++ b/nixos/tests/php/fpm.nix
@@ -43,7 +43,7 @@ import ../make-test-python.nix ({pkgs, lib, ...}: {
     machine.wait_for_unit("phpfpm-foobar.service")
 
     # Check so we get an evaluated PHP back
-    response = machine.succeed("curl -vvv -s http://127.0.0.1:80/")
+    response = machine.succeed("curl -fvvv -s http://127.0.0.1:80/")
     assert "PHP Version ${pkgs.php.version}" in response, "PHP version not detected"
 
     # Check so we have database and some other extensions loaded
diff --git a/nixos/tests/php/httpd.nix b/nixos/tests/php/httpd.nix
index 1092e0ecadd..27ea7a24e3a 100644
--- a/nixos/tests/php/httpd.nix
+++ b/nixos/tests/php/httpd.nix
@@ -21,7 +21,7 @@ import ../make-test-python.nix ({pkgs, lib, ...}: {
     machine.wait_for_unit("httpd.service")
 
     # Check so we get an evaluated PHP back
-    response = machine.succeed("curl -vvv -s http://127.0.0.1:80/")
+    response = machine.succeed("curl -fvvv -s http://127.0.0.1:80/")
     assert "PHP Version ${pkgs.php.version}" in response, "PHP version not detected"
 
     # Check so we have database and some other extensions loaded
diff --git a/nixos/tests/php/pcre.nix b/nixos/tests/php/pcre.nix
index 3dd0964e60f..3ea19304bff 100644
--- a/nixos/tests/php/pcre.nix
+++ b/nixos/tests/php/pcre.nix
@@ -32,7 +32,7 @@ in import ../make-test-python.nix ({lib, ...}: {
     ''
       machine.wait_for_unit("httpd.service")
       # Ensure php evaluation by matching on the var_dump syntax
-      response = machine.succeed("curl -vvv -s http://127.0.0.1:80/index.php")
+      response = machine.succeed("curl -fvvv -s http://127.0.0.1:80/index.php")
       expected = 'string(${toString (builtins.stringLength testString)}) "${testString}"'
       assert expected in response, "Does not appear to be able to use subgroups."
     '';
diff --git a/nixos/tests/prometheus.nix b/nixos/tests/prometheus.nix
index af2aa66a552..de1d10aa7ca 100644
--- a/nixos/tests/prometheus.nix
+++ b/nixos/tests/prometheus.nix
@@ -193,13 +193,13 @@ in import ./make-test-python.nix {
     # Check if prometheus responds to requests:
     prometheus.wait_for_unit("prometheus.service")
     prometheus.wait_for_open_port(${toString queryPort})
-    prometheus.succeed("curl -s http://127.0.0.1:${toString queryPort}/metrics")
+    prometheus.succeed("curl -sf http://127.0.0.1:${toString queryPort}/metrics")
 
     # Let's test if pushing a metric to the pushgateway succeeds:
     prometheus.wait_for_unit("pushgateway.service")
     prometheus.succeed(
         "echo 'some_metric 3.14' | "
-        + "curl --data-binary \@- "
+        + "curl -f --data-binary \@- "
         + "http://127.0.0.1:${toString pushgwPort}/metrics/job/some_job"
     )
 
diff --git a/nixos/tests/sanoid.nix b/nixos/tests/sanoid.nix
index 284b38932cc..66ddaad60ea 100644
--- a/nixos/tests/sanoid.nix
+++ b/nixos/tests/sanoid.nix
@@ -38,7 +38,7 @@ in {
 
       services.syncoid = {
         enable = true;
-        sshKey = "/root/.ssh/id_ecdsa";
+        sshKey = "/var/lib/syncoid/id_ecdsa";
         commonArgs = [ "--no-sync-snap" ];
         commands."pool/test".target = "root@target:pool/test";
       };
@@ -69,11 +69,12 @@ in {
         "udevadm settle",
     )
 
-    source.succeed("mkdir -m 700 /root/.ssh")
     source.succeed(
-        "cat '${snakeOilPrivateKey}' > /root/.ssh/id_ecdsa"
+        "mkdir -m 700 -p /var/lib/syncoid",
+        "cat '${snakeOilPrivateKey}' > /var/lib/syncoid/id_ecdsa",
+        "chmod 600 /var/lib/syncoid/id_ecdsa",
+        "chown -R syncoid:syncoid /var/lib/syncoid/",
     )
-    source.succeed("chmod 600 /root/.ssh/id_ecdsa")
 
     source.succeed("touch /tmp/mnt/test.txt")
     source.systemctl("start --wait sanoid.service")
diff --git a/nixos/tests/service-runner.nix b/nixos/tests/service-runner.nix
index 39ae66fe111..55fbbb72934 100644
--- a/nixos/tests/service-runner.nix
+++ b/nixos/tests/service-runner.nix
@@ -29,7 +29,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
             """
         )
         machine.wait_for_open_port(80)
-        machine.succeed(f"curl {url}")
+        machine.succeed(f"curl -f {url}")
         machine.succeed("kill -INT $(cat my-nginx.pid)")
         machine.wait_for_closed_port(80)
   '';
diff --git a/nixos/tests/spacecookie.nix b/nixos/tests/spacecookie.nix
index 6eff32a2e75..5b5022a7427 100644
--- a/nixos/tests/spacecookie.nix
+++ b/nixos/tests/spacecookie.nix
@@ -32,7 +32,7 @@ in
       ${gopherHost}.wait_for_unit("spacecookie.service")
       client.wait_for_unit("network.target")
 
-      fileResponse = client.succeed("curl -s gopher://${gopherHost}//${fileName}")
+      fileResponse = client.succeed("curl -f -s gopher://${gopherHost}//${fileName}")
 
       # the file response should return our created file exactly
       if not (fileResponse == "${fileContent}\n"):
@@ -41,7 +41,7 @@ in
       # sanity check on the directory listing: we serve a directory and a file
       # via gopher, so the directory listing should have exactly two entries,
       # one with gopher file type 0 (file) and one with file type 1 (directory).
-      dirResponse = client.succeed("curl -s gopher://${gopherHost}")
+      dirResponse = client.succeed("curl -f -s gopher://${gopherHost}")
       dirEntries = [l[0] for l in dirResponse.split("\n") if len(l) > 0]
       dirEntries.sort()
 
diff --git a/nixos/tests/sslh.nix b/nixos/tests/sslh.nix
index 2a800aa52d0..17094606e8e 100644
--- a/nixos/tests/sslh.nix
+++ b/nixos/tests/sslh.nix
@@ -78,6 +78,6 @@ import ./make-test-python.nix {
         server.succeed(f"grep '{ip}' /tmp/foo{arg}")
 
         # check that http through sslh works
-        assert client.succeed(f"curl {arg} http://server:443").strip() == "hello world"
+        assert client.succeed(f"curl -f {arg} http://server:443").strip() == "hello world"
   '';
 }
diff --git a/nixos/tests/sympa.nix b/nixos/tests/sympa.nix
index 280691f7cb4..eb38df180a7 100644
--- a/nixos/tests/sympa.nix
+++ b/nixos/tests/sympa.nix
@@ -30,7 +30,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     machine.wait_for_unit("sympa.service")
     machine.wait_for_unit("wwsympa.service")
     assert "Mailing lists service" in machine.succeed(
-        "curl --insecure -L http://localhost/"
+        "curl --fail --insecure -L http://localhost/"
     )
   '';
 })
diff --git a/nixos/tests/syncthing-relay.nix b/nixos/tests/syncthing-relay.nix
index cd72ef1cbe1..c144bf7fca3 100644
--- a/nixos/tests/syncthing-relay.nix
+++ b/nixos/tests/syncthing-relay.nix
@@ -19,7 +19,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
     machine.wait_for_open_port(12346)
 
     out = machine.succeed(
-        "curl -sS http://localhost:12346/status | jq -r '.options.\"provided-by\"'"
+        "curl -sSf http://localhost:12346/status | jq -r '.options.\"provided-by\"'"
     )
     assert "nixos-test" in out
   '';
diff --git a/nixos/tests/syncthing.nix b/nixos/tests/syncthing.nix
index 9e2a8e01e3f..ac9df5e50c8 100644
--- a/nixos/tests/syncthing.nix
+++ b/nixos/tests/syncthing.nix
@@ -25,7 +25,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
             "xmllint --xpath 'string(configuration/gui/apikey)' %s/config.xml" % confdir
         ).strip()
         oldConf = host.succeed(
-            "curl -Ss -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config" % APIKey
+            "curl -Ssf -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config" % APIKey
         )
         conf = json.loads(oldConf)
         conf["devices"].append({"deviceID": deviceID, "id": name})
@@ -39,7 +39,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
         )
         newConf = json.dumps(conf)
         host.succeed(
-            "curl -Ss -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config -d %s"
+            "curl -Ssf -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config -d %s"
             % (APIKey, shlex.quote(newConf))
         )
 
diff --git a/nixos/tests/trac.nix b/nixos/tests/trac.nix
index 7953f8d41f7..af7182d1e18 100644
--- a/nixos/tests/trac.nix
+++ b/nixos/tests/trac.nix
@@ -14,6 +14,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     start_all()
     machine.wait_for_unit("trac.service")
     machine.wait_for_open_port(8000)
-    machine.wait_until_succeeds("curl -L http://localhost:8000/ | grep 'Trac Powered'")
+    machine.wait_until_succeeds("curl -fL http://localhost:8000/ | grep 'Trac Powered'")
   '';
 })
diff --git a/nixos/tests/trezord.nix b/nixos/tests/trezord.nix
index b7b3dd31942..7c8370f409e 100644
--- a/nixos/tests/trezord.nix
+++ b/nixos/tests/trezord.nix
@@ -14,6 +14,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     start_all()
     machine.wait_for_unit("trezord.service")
     machine.wait_for_open_port(21325)
-    machine.wait_until_succeeds("curl -L http://localhost:21325/status/ | grep Version")
+    machine.wait_until_succeeds("curl -fL http://localhost:21325/status/ | grep Version")
   '';
 })
diff --git a/nixos/tests/trickster.nix b/nixos/tests/trickster.nix
index 713ac8f0b2f..e32f919a1ad 100644
--- a/nixos/tests/trickster.nix
+++ b/nixos/tests/trickster.nix
@@ -19,19 +19,19 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     prometheus.wait_for_unit("prometheus.service")
     prometheus.wait_for_open_port(9090)
     prometheus.wait_until_succeeds(
-        "curl -L http://localhost:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
+        "curl -fL http://localhost:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
     )
     trickster.wait_for_unit("trickster.service")
     trickster.wait_for_open_port(8082)
     trickster.wait_for_open_port(9090)
     trickster.wait_until_succeeds(
-        "curl -L http://localhost:8082/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
+        "curl -fL http://localhost:8082/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
     )
     trickster.wait_until_succeeds(
-        "curl -L http://prometheus:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
+        "curl -fL http://prometheus:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
     )
     trickster.wait_until_succeeds(
-        "curl -L http://localhost:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
+        "curl -fL http://localhost:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'"
     )
   '';
 })
diff --git a/nixos/tests/upnp.nix b/nixos/tests/upnp.nix
index a7d837ea070..046c0a56b2a 100644
--- a/nixos/tests/upnp.nix
+++ b/nixos/tests/upnp.nix
@@ -90,7 +90,7 @@ in
       client1.succeed("upnpc -a ${internalClient1Address} 9000 9000 TCP")
 
       client1.wait_for_unit("httpd")
-      client2.wait_until_succeeds("curl http://${externalRouterAddress}:9000/")
+      client2.wait_until_succeeds("curl -f http://${externalRouterAddress}:9000/")
     '';
 
 })
diff --git a/nixos/tests/uwsgi.nix b/nixos/tests/uwsgi.nix
index 78a87147f55..7f4945a8803 100644
--- a/nixos/tests/uwsgi.nix
+++ b/nixos/tests/uwsgi.nix
@@ -33,6 +33,6 @@ import ./make-test-python.nix ({ pkgs, ... }:
       machine.wait_for_unit("multi-user.target")
       machine.wait_for_unit("uwsgi.service")
       machine.wait_for_open_port(8000)
-      assert "Hello World" in machine.succeed("curl -v 127.0.0.1:8000")
+      assert "Hello World" in machine.succeed("curl -fv 127.0.0.1:8000")
     '';
 })
diff --git a/nixos/tests/victoriametrics.nix b/nixos/tests/victoriametrics.nix
index 73ef8b72861..fff8d7005da 100644
--- a/nixos/tests/victoriametrics.nix
+++ b/nixos/tests/victoriametrics.nix
@@ -19,9 +19,11 @@ import ./make-test-python.nix ({ pkgs, ...} : {
 
     # write some points and run simple query
     out = one.succeed(
-        "curl -d 'measurement,tag1=value1,tag2=value2 field1=123,field2=1.23' -X POST 'http://localhost:8428/write'"
+        "curl -f -d 'measurement,tag1=value1,tag2=value2 field1=123,field2=1.23' -X POST 'http://localhost:8428/write'"
+    )
+    cmd = (
+        """curl -f -s -G 'http://localhost:8428/api/v1/export' -d 'match={__name__!=""}'"""
     )
-    cmd = """curl -s -G 'http://localhost:8428/api/v1/export' -d 'match={__name__!=""}'"""
     # data takes a while to appear
     one.wait_until_succeeds(f"[[ $({cmd} | wc -l) -ne 0 ]]")
     out = one.succeed(cmd)
diff --git a/nixos/tests/web-servers/unit-php.nix b/nixos/tests/web-servers/unit-php.nix
index 2a0a5bdaa5d..033036ee766 100644
--- a/nixos/tests/web-servers/unit-php.nix
+++ b/nixos/tests/web-servers/unit-php.nix
@@ -47,7 +47,7 @@ in {
     machine.wait_for_unit("unit.service")
 
     # Check so we get an evaluated PHP back
-    response = machine.succeed("curl -vvv -s http://127.0.0.1:9074/")
+    response = machine.succeed("curl -f -vvv -s http://127.0.0.1:9074/")
     assert "PHP Version ${pkgs.unit.usedPhp74.version}" in response, "PHP version not detected"
 
     # Check so we have database and some other extensions loaded
diff --git a/nixos/tests/wordpress.nix b/nixos/tests/wordpress.nix
index b7449859f7e..5d740502bb5 100644
--- a/nixos/tests/wordpress.nix
+++ b/nixos/tests/wordpress.nix
@@ -40,7 +40,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
 
     with subtest("website returns welcome screen"):
         for site_name in site_names:
-            assert "Welcome to the famous" in machine.succeed(f"curl -L {site_name}")
+            assert "Welcome to the famous" in machine.succeed(f"curl -fL {site_name}")
 
     with subtest("wordpress-init went through"):
         for site_name in site_names: