summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorPeter Hoeg <peter@hoeg.com>2018-10-05 10:08:56 +0800
committerGitHub <noreply@github.com>2018-10-05 10:08:56 +0800
commitc81d370bb96a834f176f5ded3eb3a56d19b07426 (patch)
tree7325df02c333ebf602d9553e7176f6baac82852d /nixos
parent4dada63a174331151251cf8be093cd8fcdf28c12 (diff)
parent8a4a732e997201bc02d4b5b16756e81b83df797e (diff)
downloadnixpkgs-c81d370bb96a834f176f5ded3eb3a56d19b07426.tar
nixpkgs-c81d370bb96a834f176f5ded3eb3a56d19b07426.tar.gz
nixpkgs-c81d370bb96a834f176f5ded3eb3a56d19b07426.tar.bz2
nixpkgs-c81d370bb96a834f176f5ded3eb3a56d19b07426.tar.lz
nixpkgs-c81d370bb96a834f176f5ded3eb3a56d19b07426.tar.xz
nixpkgs-c81d370bb96a834f176f5ded3eb3a56d19b07426.tar.zst
nixpkgs-c81d370bb96a834f176f5ded3eb3a56d19b07426.zip
Merge branch 'master' into f/activation
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/Makefile2
-rw-r--r--nixos/doc/manual/installation/installing-usb.xml48
-rw-r--r--nixos/doc/manual/installation/installing.xml707
-rw-r--r--nixos/modules/hardware/opengl.nix22
-rw-r--r--nixos/modules/hardware/steam-hardware.nix25
-rw-r--r--nixos/modules/hardware/video/nvidia.nix120
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix8
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl3
-rw-r--r--nixos/modules/installer/tools/nixos-option.sh4
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/security/pam.nix7
-rw-r--r--nixos/modules/security/polkit.nix10
-rw-r--r--nixos/modules/services/networking/bitlbee.nix41
-rw-r--r--nixos/modules/services/security/clamav.nix7
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix463
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix17
-rw-r--r--nixos/modules/services/web-servers/nginx/location-options.nix10
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix11
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix17
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix16
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix4
-rw-r--r--nixos/modules/services/x11/xserver.nix10
-rw-r--r--nixos/modules/system/activation/activation-script.nix8
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl4
-rw-r--r--nixos/modules/system/activation/top-level.nix1
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh8
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix2
-rw-r--r--nixos/modules/system/boot/systemd.nix14
-rw-r--r--nixos/modules/virtualisation/hyperv-guest.nix35
-rw-r--r--nixos/release.nix1
-rw-r--r--nixos/tests/misc.nix2
-rw-r--r--nixos/tests/nextcloud/basic.nix56
-rw-r--r--nixos/tests/nextcloud/default.nix6
-rw-r--r--nixos/tests/nextcloud/with-mysql-and-memcached.nix97
-rw-r--r--nixos/tests/nextcloud/with-postgresql-and-redis.nix130
-rw-r--r--nixos/tests/nix-ssh-serve.nix4
37 files changed, 1507 insertions, 417 deletions
diff --git a/nixos/doc/manual/Makefile b/nixos/doc/manual/Makefile
index 2e9adf70c39..b251a1f5e2c 100644
--- a/nixos/doc/manual/Makefile
+++ b/nixos/doc/manual/Makefile
@@ -4,7 +4,7 @@ all: manual-combined.xml format
 .PHONY: debug
 debug: generated manual-combined.xml
 
-manual-combined.xml: generated *.xml
+manual-combined.xml: generated *.xml **/*.xml
 	rm -f ./manual-combined.xml
 	nix-shell --packages xmloscopy \
 		--run "xmloscopy --docbook5 ./manual.xml ./manual-combined.xml"
diff --git a/nixos/doc/manual/installation/installing-usb.xml b/nixos/doc/manual/installation/installing-usb.xml
index c5934111749..0b311189430 100644
--- a/nixos/doc/manual/installation/installing-usb.xml
+++ b/nixos/doc/manual/installation/installing-usb.xml
@@ -9,13 +9,12 @@
   For systems without CD drive, the NixOS live CD can be booted from a USB
   stick. You can use the <command>dd</command> utility to write the image:
   <command>dd if=<replaceable>path-to-image</replaceable>
-  of=<replaceable>/dev/sdb</replaceable></command>. Be careful about specifying
+  of=<replaceable>/dev/sdX</replaceable></command>. Be careful about specifying
   the correct drive; you can use the <command>lsblk</command> command to get a
   list of block devices.
- </para>
-
- <para>
-  On macOS:
+  <note>
+   <title>On macOS</title>
+   <para>
 <programlisting>
 $ diskutil list
 [..]
@@ -26,43 +25,16 @@ $ diskutil unmountDisk diskN
 Unmount of all volumes on diskN was successful
 $ sudo dd bs=1m if=nix.iso of=/dev/rdiskN
 </programlisting>
-  Using the 'raw' <command>rdiskN</command> device instead of
-  <command>diskN</command> completes in minutes instead of hours. After
-  <command>dd</command> completes, a GUI dialog "The disk you inserted was not
-  readable by this computer" will pop up, which can be ignored.
+    Using the 'raw' <command>rdiskN</command> device instead of
+    <command>diskN</command> completes in minutes instead of hours. After
+    <command>dd</command> completes, a GUI dialog "The disk you inserted was
+    not readable by this computer" will pop up, which can be ignored.
+   </para>
+  </note>
  </para>
 
  <para>
   The <command>dd</command> utility will write the image verbatim to the drive,
   making it the recommended option for both UEFI and non-UEFI installations.
-  For non-UEFI installations, you can alternatively use
-  <link xlink:href="http://unetbootin.sourceforge.net/">unetbootin</link>. If
-  you cannot use <command>dd</command> for a UEFI installation, you can also
-  mount the ISO, copy its contents verbatim to your drive, then either:
-  <itemizedlist>
-   <listitem>
-    <para>
-     Change the label of the disk partition to the label of the ISO (visible
-     with the blkid command), or
-    </para>
-   </listitem>
-   <listitem>
-    <para>
-     Edit <filename>loader/entries/nixos-livecd.conf</filename> on the drive
-     and change the <literal>root=</literal> field in the
-     <literal>options</literal> line to point to your drive (see the
-     documentation on <literal>root=</literal> in
-     <link xlink:href="https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt">
-     the kernel documentation</link> for more details).
-    </para>
-   </listitem>
-   <listitem>
-    <para>
-     If you want to load the contents of the ISO to ram after bootin (So you
-     can remove the stick after bootup) you can append the parameter
-     <literal>copytoram</literal> to the <literal>options</literal> field.
-    </para>
-   </listitem>
-  </itemizedlist>
  </para>
 </section>
diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml
index 1366e8f9359..2b68def95b7 100644
--- a/nixos/doc/manual/installation/installing.xml
+++ b/nixos/doc/manual/installation/installing.xml
@@ -4,60 +4,46 @@
             version="5.0"
             xml:id="sec-installation">
  <title>Installing NixOS</title>
- <para>
-  NixOS can be installed on BIOS or UEFI systems. The procedure for a UEFI
-  installation is by and large the same as a BIOS installation. The differences
-  are mentioned in the steps that follow.
- </para>
- <orderedlist>
-  <listitem>
-   <para>
-    Boot from the CD.
-   </para>
-   <variablelist>
-    <varlistentry>
-     <term>
-      UEFI systems
-     </term>
-     <listitem>
-      <para>
-       You should boot the live CD in UEFI mode (consult your specific
-       hardware's documentation for instructions). You may find the
-       <link xlink:href="http://www.rodsbooks.com/refind">rEFInd boot
-       manager</link> useful.
-      </para>
-     </listitem>
-    </varlistentry>
-   </variablelist>
-  </listitem>
-  <listitem>
-   <para>
-    The CD contains a basic NixOS installation. (It also contains Memtest86+,
-    useful if you want to test new hardware). When it’s finished booting, it
-    should have detected most of your hardware.
-   </para>
-  </listitem>
-  <listitem>
-   <para>
-    The NixOS manual is available on virtual console 8 (press Alt+F8 to access)
-    or by running <command>nixos-help</command>.
-   </para>
-  </listitem>
-  <listitem>
-   <para>
-    You get logged in as <literal>root</literal> (with empty password).
-   </para>
-  </listitem>
-  <listitem>
-   <para>
-    If you downloaded the graphical ISO image, you can run <command>systemctl
-    start display-manager</command> to start KDE. If you want to continue on
-    the terminal, you can use <command>loadkeys</command> to switch to your
-    preferred keyboard layout. (We even provide neo2 via <command>loadkeys de
-    neo</command>!)
-   </para>
-  </listitem>
-  <listitem>
+ <section xml:id="sec-installation-booting">
+  <title>Booting the system</title>
+
+  <para>
+   NixOS can be installed on BIOS or UEFI systems. The procedure for a UEFI
+   installation is by and large the same as a BIOS installation. The
+   differences are mentioned in the steps that follow.
+  </para>
+
+  <para>
+   The installation media can be burned to a CD, or now more commonly, "burned"
+   to a USB drive (see <xref linkend="sec-booting-from-usb"/>).
+  </para>
+
+  <para>
+   The installation media contains a basic NixOS installation. When it’s
+   finished booting, it should have detected most of your hardware.
+  </para>
+
+  <para>
+   The NixOS manual is available on virtual console 8 (press Alt+F8 to access)
+   or by running <command>nixos-help</command>.
+  </para>
+
+  <para>
+   You are logged-in automatically as <literal>root</literal>. (The
+   <literal>root</literal> user account has an empty password.)
+  </para>
+
+  <para>
+   If you downloaded the graphical ISO image, you can run <command>systemctl
+   start display-manager</command> to start KDE. If you want to continue on the
+   terminal, you can use <command>loadkeys</command> to switch to your
+   preferred keyboard layout. (We even provide neo2 via <command>loadkeys de
+   neo</command>!)
+  </para>
+
+  <section xml:id="sec-installation-booting-networking">
+   <title>Networking in the installer</title>
+
    <para>
     The boot process should have brought up networking (check <command>ip
     a</command>). Networking is necessary for the installer, since it will
@@ -65,60 +51,167 @@
     binaries). It’s best if you have a DHCP server on your network. Otherwise
     configure networking manually using <command>ifconfig</command>.
    </para>
+
    <para>
     To manually configure the network on the graphical installer, first disable
     network-manager with <command>systemctl stop network-manager</command>.
    </para>
+
    <para>
     To manually configure the wifi on the minimal installer, run
     <command>wpa_supplicant -B -i interface -c &lt;(wpa_passphrase 'SSID'
     'key')</command>.
    </para>
-  </listitem>
-  <listitem>
+
    <para>
     If you would like to continue the installation from a different machine you
     need to activate the SSH daemon via <literal>systemctl start
     sshd</literal>. In order to be able to login you also need to set a
     password for <literal>root</literal> using <literal>passwd</literal>.
    </para>
-  </listitem>
-  <listitem>
+  </section>
+ </section>
+ <section xml:id="sec-installation-partitioning">
+  <title>Partitioning and formatting</title>
+
+  <para>
+   The NixOS installer doesn’t do any partitioning or formatting, so you need
+   to do that yourself.
+  </para>
+
+  <para>
+   The NixOS installer ships with multiple partitioning tools. The examples
+   below use <command>parted</command>, but also provides
+   <command>fdisk</command>, <command>gdisk</command>,
+   <command>cfdisk</command>, and <command>cgdisk</command>.
+  </para>
+
+  <para>
+   The recommended partition scheme differs depending if the computer uses
+   <emphasis>Legacy Boot</emphasis> or <emphasis>UEFI</emphasis>.
+  </para>
+
+  <section xml:id="sec-installation-partitioning-UEFI">
+   <title>UEFI (GPT)</title>
+
    <para>
-    The NixOS installer doesn’t do any partitioning or formatting yet, so you
-    need to do that yourself. Use the following commands:
-    <itemizedlist>
+    Here's an example partition scheme for UEFI, using
+    <filename>/dev/sda</filename> as the device.
+    <note>
+     <para>
+      You can safely ignore <command>parted</command>'s informational message
+      about needing to update /etc/fstab.
+     </para>
+    </note>
+   </para>
+
+   <para>
+    <orderedlist>
      <listitem>
       <para>
-       For partitioning: <command>fdisk</command>.
-<screen>
-# fdisk /dev/sda # <lineannotation>(or whatever device you want to install on)</lineannotation>
--- for UEFI systems only
-> n      # <lineannotation>(create a new partition for /boot)</lineannotation>
-> 3      # <lineannotation>(make it a partition number 3)</lineannotation>
->        # <lineannotation>(press enter to accept the default)</lineannotation>
-> +512M  # <lineannotation>(the size of the UEFI boot partition)</lineannotation>
-> t      # <lineannotation>(change the partition type ...)</lineannotation>
-> 3      # <lineannotation>(... of the boot partition ...)</lineannotation>
-> 1      # <lineannotation>(... to 'UEFI System')</lineannotation>
--- for BIOS or UEFI systems
-> n      # <lineannotation>(create a new partition for /swap)</lineannotation>
-> 2      # <lineannotation>(make it a partition number 2)</lineannotation>
->        # <lineannotation>(press enter to accept the default)</lineannotation>
-> +8G    # <lineannotation>(the size of the swap partition, set to whatever you like)</lineannotation>
-> n      # <lineannotation>(create a new partition for /)</lineannotation>
-> 1      # <lineannotation>(make it a partition number 1)</lineannotation>
->        # <lineannotation>(press enter to accept the default)</lineannotation>
->        # <lineannotation>(press enter to accept the default and use the rest of the remaining space)</lineannotation>
-> a      # <lineannotation>(make the partition bootable)</lineannotation>
-> x      # <lineannotation>(enter expert mode)</lineannotation>
-> f      # <lineannotation>(fix up the partition ordering)</lineannotation>
-> r      # <lineannotation>(exit expert mode)</lineannotation>
-> w      # <lineannotation>(write the partition table to disk and exit)</lineannotation></screen>
+       Create a <emphasis>GPT</emphasis> partition table.
+<screen language="commands"># parted /dev/sda -- mklabel gpt</screen>
       </para>
      </listitem>
      <listitem>
       <para>
+       Add a <emphasis>swap</emphasis> partition. The size required will vary
+       according to needs, here a 8GiB one is created. The space left in front
+       (512MiB) will be used by the boot partition.
+<screen language="commands"># parted /dev/sda -- mkpart primary linux-swap 512MiB 8.5GiB</screen>
+       <note>
+        <para>
+         The swap partition size rules are no different than for other Linux
+         distributions.
+        </para>
+       </note>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Next, add the <emphasis>root</emphasis> partition. This will fill the
+       remainder ending part of the disk.
+<screen language="commands"># parted /dev/sda -- mkpart primary 8.5GiB -1MiB</screen>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Finally, the <emphasis>boot</emphasis> partition. NixOS by default uses
+       the ESP (EFI system partition) as its <emphasis>/boot</emphasis>
+       partition. It uses the initially reserved 512MiB at the start of the
+       disk.
+<screen language="commands"># parted /dev/sda -- mkpart ESP fat32 1M 512MiB
+# parted /dev/sda -- set 3 boot on</screen>
+      </para>
+     </listitem>
+    </orderedlist>
+   </para>
+
+   <para>
+    Once complete, you can follow with
+    <xref linkend="sec-installation-partitioning-formatting"/>.
+   </para>
+  </section>
+
+  <section xml:id="sec-installation-partitioning-MBR">
+   <title>Legacy Boot (MBR)</title>
+
+   <para>
+    Here's an example partition scheme for Legacy Boot, using
+    <filename>/dev/sda</filename> as the device.
+    <note>
+     <para>
+      You can safely ignore <command>parted</command>'s informational message
+      about needing to update /etc/fstab.
+     </para>
+    </note>
+   </para>
+
+   <para>
+    <orderedlist>
+     <listitem>
+      <para>
+       Create a <emphasis>MBR</emphasis> partition table.
+<screen language="commands"># parted /dev/sda -- mklabel msdos</screen>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Add a <emphasis>swap</emphasis> partition. The size required will vary
+       according to needs, here a 8GiB one is created.
+<screen language="commands"># parted /dev/sda -- mkpart primary linux-swap 1M 8GiB</screen>
+       <note>
+        <para>
+         The swap partition size rules are no different than for other Linux
+         distributions.
+        </para>
+       </note>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Finally, add the <emphasis>root</emphasis> partition. This will fill the
+       remainder of the disk.
+<screen language="commands"># parted /dev/sda -- mkpart primary 8GiB -1s</screen>
+      </para>
+     </listitem>
+    </orderedlist>
+   </para>
+
+   <para>
+    Once complete, you can follow with
+    <xref linkend="sec-installation-partitioning-formatting"/>.
+   </para>
+  </section>
+
+  <section xml:id="sec-installation-partitioning-formatting">
+   <title>Formatting</title>
+
+   <para>
+    Use the following commands:
+    <itemizedlist>
+     <listitem>
+      <para>
        For initialising Ext4 partitions: <command>mkfs.ext4</command>. It is
        recommended that you assign a unique symbolic label to the file system
        using the option <option>-L <replaceable>label</replaceable></option>,
@@ -169,242 +262,249 @@
      </listitem>
     </itemizedlist>
    </para>
-  </listitem>
-  <listitem>
-   <para>
-    Mount the target file system on which NixOS should be installed on
-    <filename>/mnt</filename>, e.g.
+  </section>
+ </section>
+ <section xml:id="sec-installation-installing">
+  <title>Installing</title>
+
+  <orderedlist>
+   <listitem>
+    <para>
+     Mount the target file system on which NixOS should be installed on
+     <filename>/mnt</filename>, e.g.
 <screen>
 # mount /dev/disk/by-label/nixos /mnt
 </screen>
-   </para>
-  </listitem>
-  <listitem>
-   <variablelist>
-    <varlistentry>
-     <term>
-      UEFI systems
-     </term>
-     <listitem>
-      <para>
-       Mount the boot file system on <filename>/mnt/boot</filename>, e.g.
+    </para>
+   </listitem>
+   <listitem>
+    <variablelist>
+     <varlistentry>
+      <term>
+       UEFI systems
+      </term>
+      <listitem>
+       <para>
+        Mount the boot file system on <filename>/mnt/boot</filename>, e.g.
 <screen>
 # mkdir -p /mnt/boot
 # mount /dev/disk/by-label/boot /mnt/boot
 </screen>
-      </para>
-     </listitem>
-    </varlistentry>
-   </variablelist>
-  </listitem>
-  <listitem>
-   <para>
-    If your machine has a limited amount of memory, you may want to activate
-    swap devices now (<command>swapon
-    <replaceable>device</replaceable></command>). The installer (or rather, the
-    build actions that it may spawn) may need quite a bit of RAM, depending on
-    your configuration.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </listitem>
+   <listitem>
+    <para>
+     If your machine has a limited amount of memory, you may want to activate
+     swap devices now (<command>swapon
+     <replaceable>device</replaceable></command>). The installer (or rather,
+     the build actions that it may spawn) may need quite a bit of RAM,
+     depending on your configuration.
 <screen>
 # swapon /dev/sda2</screen>
-   </para>
-  </listitem>
-  <listitem>
-   <para>
-    You now need to create a file
-    <filename>/mnt/etc/nixos/configuration.nix</filename> that specifies the
-    intended configuration of the system. This is because NixOS has a
-    <emphasis>declarative</emphasis> configuration model: you create or edit a
-    description of the desired configuration of your system, and then NixOS
-    takes care of making it happen. The syntax of the NixOS configuration file
-    is described in <xref linkend="sec-configuration-syntax"/>, while a list of
-    available configuration options appears in
-    <xref
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     You now need to create a file
+     <filename>/mnt/etc/nixos/configuration.nix</filename> that specifies the
+     intended configuration of the system. This is because NixOS has a
+     <emphasis>declarative</emphasis> configuration model: you create or edit a
+     description of the desired configuration of your system, and then NixOS
+     takes care of making it happen. The syntax of the NixOS configuration file
+     is described in <xref linkend="sec-configuration-syntax"/>, while a list
+     of available configuration options appears in
+     <xref
     linkend="ch-options"/>. A minimal example is shown in
-    <xref
+     <xref
     linkend="ex-config"/>.
-   </para>
-   <para>
-    The command <command>nixos-generate-config</command> can generate an
-    initial configuration file for you:
+    </para>
+    <para>
+     The command <command>nixos-generate-config</command> can generate an
+     initial configuration file for you:
 <screen>
 # nixos-generate-config --root /mnt</screen>
-    You should then edit <filename>/mnt/etc/nixos/configuration.nix</filename>
-    to suit your needs:
+     You should then edit <filename>/mnt/etc/nixos/configuration.nix</filename>
+     to suit your needs:
 <screen>
 # nano /mnt/etc/nixos/configuration.nix
 </screen>
-    If you’re using the graphical ISO image, other editors may be available
-    (such as <command>vim</command>). If you have network access, you can also
-    install other editors — for instance, you can install Emacs by running
-    <literal>nix-env -i emacs</literal>.
-   </para>
-   <variablelist>
-    <varlistentry>
-     <term>
-      BIOS systems
-     </term>
-     <listitem>
-      <para>
-       You <emphasis>must</emphasis> set the option
-       <xref linkend="opt-boot.loader.grub.device"/> to specify on which disk
-       the GRUB boot loader is to be installed. Without it, NixOS cannot boot.
-      </para>
-     </listitem>
-    </varlistentry>
-    <varlistentry>
-     <term>
-      UEFI systems
-     </term>
-     <listitem>
-      <para>
-       You <emphasis>must</emphasis> set the option
-       <xref linkend="opt-boot.loader.systemd-boot.enable"/> to
-       <literal>true</literal>. <command>nixos-generate-config</command> should
-       do this automatically for new configurations when booted in UEFI mode.
-      </para>
-      <para>
-       You may want to look at the options starting with
-       <option><link linkend="opt-boot.loader.efi.canTouchEfiVariables">boot.loader.efi</link></option>
-       and
-       <option><link linkend="opt-boot.loader.systemd-boot.enable">boot.loader.systemd</link></option>
-       as well.
-      </para>
-     </listitem>
-    </varlistentry>
-   </variablelist>
-   <para>
-    If there are other operating systems running on the machine before
-    installing NixOS, the <xref linkend="opt-boot.loader.grub.useOSProber"/>
-    option can be set to <literal>true</literal> to automatically add them to
-    the grub menu.
-   </para>
-   <para>
-    Another critical option is <option>fileSystems</option>, specifying the
-    file systems that need to be mounted by NixOS. However, you typically
-    don’t need to set it yourself, because
-    <command>nixos-generate-config</command> sets it automatically in
-    <filename>/mnt/etc/nixos/hardware-configuration.nix</filename> from your
-    currently mounted file systems. (The configuration file
-    <filename>hardware-configuration.nix</filename> is included from
-    <filename>configuration.nix</filename> and will be overwritten by future
-    invocations of <command>nixos-generate-config</command>; thus, you
-    generally should not modify it.)
-   </para>
-   <note>
+     If you’re using the graphical ISO image, other editors may be available
+     (such as <command>vim</command>). If you have network access, you can also
+     install other editors — for instance, you can install Emacs by running
+     <literal>nix-env -i emacs</literal>.
+    </para>
+    <variablelist>
+     <varlistentry>
+      <term>
+       BIOS systems
+      </term>
+      <listitem>
+       <para>
+        You <emphasis>must</emphasis> set the option
+        <xref linkend="opt-boot.loader.grub.device"/> to specify on which disk
+        the GRUB boot loader is to be installed. Without it, NixOS cannot boot.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term>
+       UEFI systems
+      </term>
+      <listitem>
+       <para>
+        You <emphasis>must</emphasis> set the option
+        <xref linkend="opt-boot.loader.systemd-boot.enable"/> to
+        <literal>true</literal>. <command>nixos-generate-config</command>
+        should do this automatically for new configurations when booted in UEFI
+        mode.
+       </para>
+       <para>
+        You may want to look at the options starting with
+        <option><link linkend="opt-boot.loader.efi.canTouchEfiVariables">boot.loader.efi</link></option>
+        and
+        <option><link linkend="opt-boot.loader.systemd-boot.enable">boot.loader.systemd</link></option>
+        as well.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
     <para>
-     Depending on your hardware configuration or type of file system, you may
-     need to set the option <option>boot.initrd.kernelModules</option> to
-     include the kernel modules that are necessary for mounting the root file
-     system, otherwise the installed system will not be able to boot. (If this
-     happens, boot from the CD again, mount the target file system on
-     <filename>/mnt</filename>, fix
-     <filename>/mnt/etc/nixos/configuration.nix</filename> and rerun
-     <filename>nixos-install</filename>.) In most cases,
-     <command>nixos-generate-config</command> will figure out the required
-     modules.
+     If there are other operating systems running on the machine before
+     installing NixOS, the <xref linkend="opt-boot.loader.grub.useOSProber"/>
+     option can be set to <literal>true</literal> to automatically add them to
+     the grub menu.
     </para>
-   </note>
-  </listitem>
-  <listitem>
-   <para>
-    Do the installation:
+    <para>
+     Another critical option is <option>fileSystems</option>, specifying the
+     file systems that need to be mounted by NixOS. However, you typically
+     don’t need to set it yourself, because
+     <command>nixos-generate-config</command> sets it automatically in
+     <filename>/mnt/etc/nixos/hardware-configuration.nix</filename> from your
+     currently mounted file systems. (The configuration file
+     <filename>hardware-configuration.nix</filename> is included from
+     <filename>configuration.nix</filename> and will be overwritten by future
+     invocations of <command>nixos-generate-config</command>; thus, you
+     generally should not modify it.)
+    </para>
+    <note>
+     <para>
+      Depending on your hardware configuration or type of file system, you may
+      need to set the option <option>boot.initrd.kernelModules</option> to
+      include the kernel modules that are necessary for mounting the root file
+      system, otherwise the installed system will not be able to boot. (If this
+      happens, boot from the installation media again, mount the target file
+      system on <filename>/mnt</filename>, fix
+      <filename>/mnt/etc/nixos/configuration.nix</filename> and rerun
+      <filename>nixos-install</filename>.) In most cases,
+      <command>nixos-generate-config</command> will figure out the required
+      modules.
+     </para>
+    </note>
+   </listitem>
+   <listitem>
+    <para>
+     Do the installation:
 <screen>
 # nixos-install</screen>
-    Cross fingers. If this fails due to a temporary problem (such as a network
-    issue while downloading binaries from the NixOS binary cache), you can just
-    re-run <command>nixos-install</command>. Otherwise, fix your
-    <filename>configuration.nix</filename> and then re-run
-    <command>nixos-install</command>.
-   </para>
-   <para>
-    As the last step, <command>nixos-install</command> will ask you to set the
-    password for the <literal>root</literal> user, e.g.
+     Cross fingers. If this fails due to a temporary problem (such as a network
+     issue while downloading binaries from the NixOS binary cache), you can
+     just re-run <command>nixos-install</command>. Otherwise, fix your
+     <filename>configuration.nix</filename> and then re-run
+     <command>nixos-install</command>.
+    </para>
+    <para>
+     As the last step, <command>nixos-install</command> will ask you to set the
+     password for the <literal>root</literal> user, e.g.
 <screen>
 setting root password...
 Enter new UNIX password: ***
-Retype new UNIX password: ***
-    </screen>
-    <note>
-     <para>
-      For unattended installations, it is possible to use
-      <command>nixos-install --no-root-passwd</command> in order to disable the
-      password prompt entirely.
-     </para>
-    </note>
-   </para>
-  </listitem>
-  <listitem>
-   <para>
-    If everything went well:
+Retype new UNIX password: ***</screen>
+     <note>
+      <para>
+       For unattended installations, it is possible to use
+       <command>nixos-install --no-root-passwd</command> in order to disable
+       the password prompt entirely.
+      </para>
+     </note>
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     If everything went well:
 <screen>
-        # reboot</screen>
-   </para>
-  </listitem>
-  <listitem>
-   <para>
-    You should now be able to boot into the installed NixOS. The GRUB boot menu
-    shows a list of <emphasis>available configurations</emphasis> (initially
-    just one). Every time you change the NixOS configuration (see
-    <link
+# reboot</screen>
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     You should now be able to boot into the installed NixOS. The GRUB boot
+     menu shows a list of <emphasis>available configurations</emphasis>
+     (initially just one). Every time you change the NixOS configuration (see
+     <link
         linkend="sec-changing-config">Changing Configuration</link>
-    ), a new item is added to the menu. This allows you to easily roll back to
-    a previous configuration if something goes wrong.
-   </para>
-   <para>
-    You should log in and change the <literal>root</literal> password with
-    <command>passwd</command>.
-   </para>
-   <para>
-    You’ll probably want to create some user accounts as well, which can be
-    done with <command>useradd</command>:
+     ), a new item is added to the menu. This allows you to easily roll back to
+     a previous configuration if something goes wrong.
+    </para>
+    <para>
+     You should log in and change the <literal>root</literal> password with
+     <command>passwd</command>.
+    </para>
+    <para>
+     You’ll probably want to create some user accounts as well, which can be
+     done with <command>useradd</command>:
 <screen>
 $ useradd -c 'Eelco Dolstra' -m eelco
 $ passwd eelco</screen>
-   </para>
-   <para>
-    You may also want to install some software. For instance,
+    </para>
+    <para>
+     You may also want to install some software. For instance,
 <screen>
 $ nix-env -qa \*</screen>
-    shows what packages are available, and
+     shows what packages are available, and
 <screen>
 $ nix-env -i w3m</screen>
-    install the <literal>w3m</literal> browser.
-   </para>
-  </listitem>
- </orderedlist>
- <para>
-  To summarise, <xref linkend="ex-install-sequence" /> shows a typical sequence
-  of commands for installing NixOS on an empty hard drive (here
-  <filename>/dev/sda</filename>). <xref linkend="ex-config"
+     install the <literal>w3m</literal> browser.
+    </para>
+   </listitem>
+  </orderedlist>
+ </section>
+ <section xml:id="sec-installation-summary">
+  <title>Installation summary</title>
+
+  <para>
+   To summarise, <xref linkend="ex-install-sequence" /> shows a typical
+   sequence of commands for installing NixOS on an empty hard drive (here
+   <filename>/dev/sda</filename>). <xref linkend="ex-config"
 /> shows a
-  corresponding configuration Nix expression.
- </para>
- <example xml:id='ex-install-sequence'>
-  <title>Commands for Installing NixOS on <filename>/dev/sda</filename></title>
-<screen>
-# fdisk /dev/sda # <lineannotation>(or whatever device you want to install on)</lineannotation>
--- for UEFI systems only
-> n      # <lineannotation>(create a new partition for /boot)</lineannotation>
-> 3      # <lineannotation>(make it a partition number 3)</lineannotation>
->        # <lineannotation>(press enter to accept the default)</lineannotation>
-> +512M  # <lineannotation>(the size of the UEFI boot partition)</lineannotation>
-> t      # <lineannotation>(change the partition type ...)</lineannotation>
-> 3      # <lineannotation>(... of the boot partition ...)</lineannotation>
-> 1      # <lineannotation>(... to 'UEFI System')</lineannotation>
--- for BIOS or UEFI systems
-> n      # <lineannotation>(create a new partition for /swap)</lineannotation>
-> 2      # <lineannotation>(make it a partition number 2)</lineannotation>
->        # <lineannotation>(press enter to accept the default)</lineannotation>
-> +8G    # <lineannotation>(the size of the swap partition)</lineannotation>
-> n      # <lineannotation>(create a new partition for /)</lineannotation>
-> 1      # <lineannotation>(make it a partition number 1)</lineannotation>
->        # <lineannotation>(press enter to accept the default)</lineannotation>
->        # <lineannotation>(press enter to accept the default and use the rest of the remaining space)</lineannotation>
-> a      # <lineannotation>(make the partition bootable)</lineannotation>
-> x      # <lineannotation>(enter expert mode)</lineannotation>
-> f      # <lineannotation>(fix up the partition ordering)</lineannotation>
-> r      # <lineannotation>(exit expert mode)</lineannotation>
-> w      # <lineannotation>(write the partition table to disk and exit)</lineannotation>
+   corresponding configuration Nix expression.
+  </para>
+
+  <example xml:id="ex-partition-scheme-MBR">
+   <title>Example partition schemes for NixOS on <filename>/dev/sda</filename> (MBR)</title>
+<screen language="commands">
+# parted /dev/sda -- mklabel msdos
+# parted /dev/sda -- mkpart primary linux-swap 1M 8GiB
+# parted /dev/sda -- mkpart primary 8GiB -1s</screen>
+  </example>
+
+  <example xml:id="ex-partition-scheme-UEFI">
+   <title>Example partition schemes for NixOS on <filename>/dev/sda</filename> (UEFI)</title>
+<screen language="commands">
+# parted /dev/sda -- mklabel gpt
+# parted /dev/sda -- mkpart primary linux-swap 512MiB 8.5GiB
+# parted /dev/sda -- mkpart primary 8.5GiB -1MiB
+# parted /dev/sda -- mkpart ESP fat32 1M 512MiB
+# parted /dev/sda -- set 3 boot on</screen>
+  </example>
+
+  <example xml:id="ex-install-sequence">
+   <title>Commands for Installing NixOS on <filename>/dev/sda</filename></title>
+   <para>
+    With a partitioned disk.
+<screen language="commands">
 # mkfs.ext4 -L nixos /dev/sda1
 # mkswap -L swap /dev/sda2
 # swapon /dev/sda2
@@ -416,9 +516,11 @@ $ nix-env -i w3m</screen>
 # nano /mnt/etc/nixos/configuration.nix
 # nixos-install
 # reboot</screen>
- </example>
- <example xml:id='ex-config'>
-  <title>NixOS Configuration</title>
+   </para>
+  </example>
+
+  <example xml:id='ex-config'>
+   <title>NixOS Configuration</title>
 <screen>
 { config, pkgs, ... }: {
   imports = [
@@ -438,10 +540,19 @@ $ nix-env -i w3m</screen>
   services.sshd.enable = true;
 }
   </screen>
- </example>
- <xi:include href="installing-usb.xml" />
- <xi:include href="installing-pxe.xml" />
- <xi:include href="installing-virtualbox-guest.xml" />
- <xi:include href="installing-from-other-distro.xml" />
- <xi:include href="installing-behind-a-proxy.xml" />
+  </example>
+ </section>
+ <section xml:id="sec-installation-additional-notes">
+  <title>Additional installation notes</title>
+
+  <xi:include href="installing-usb.xml" />
+
+  <xi:include href="installing-pxe.xml" />
+
+  <xi:include href="installing-virtualbox-guest.xml" />
+
+  <xi:include href="installing-from-other-distro.xml" />
+
+  <xi:include href="installing-behind-a-proxy.xml" />
+ </section>
 </chapter>
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
index b371af353cf..46d06d71333 100644
--- a/nixos/modules/hardware/opengl.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -129,17 +129,17 @@ in
       message = "Option driSupport32Bit only makes sense on a 64-bit system.";
     };
 
-    system.activationScripts.setup-opengl =
-      ''
-        ln -sfn ${package} /run/opengl-driver
-        ${if pkgs.stdenv.isi686 then ''
-          ln -sfn opengl-driver /run/opengl-driver-32
-        '' else if cfg.driSupport32Bit then ''
-          ln -sfn ${package32} /run/opengl-driver-32
-        '' else ''
-          rm -f /run/opengl-driver-32
-        ''}
-      '';
+    systemd.tmpfiles.rules = [
+      "L+ /run/opengl-driver - - - - ${package}"
+      (
+        if pkgs.stdenv.isi686 then
+          "L+ /run/opengl-driver-32 - - - - opengl-driver"
+        else if cfg.driSupport32Bit then
+          "L+ /run/opengl-driver-32 - - - - ${package32}"
+        else
+          "r /run/opengl-driver-32"
+      )
+    ];
 
     environment.sessionVariables.LD_LIBRARY_PATH =
       [ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib";
diff --git a/nixos/modules/hardware/steam-hardware.nix b/nixos/modules/hardware/steam-hardware.nix
new file mode 100644
index 00000000000..378aeffe71b
--- /dev/null
+++ b/nixos/modules/hardware/steam-hardware.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.steam-hardware;
+
+in
+
+{
+  options.hardware.steam-hardware = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable udev rules for Steam hardware such as the Steam Controller, other supported controllers and the HTC Vive";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [
+      pkgs.steamPackages.steam
+    ];
+  };
+}
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index eb195228033..6944d1a4f76 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -26,9 +26,73 @@ let
   nvidia_libs32 = (nvidiaForKernel pkgs_i686.linuxPackages).override { libsOnly = true; kernel = null; };
 
   enabled = nvidia_x11 != null;
+
+  cfg = config.hardware.nvidia;
+  optimusCfg = cfg.optimus_prime;
 in
 
 {
+  options = {
+    hardware.nvidia.modesetting.enable = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enable kernel modesetting when using the NVIDIA proprietary driver.
+
+        Enabling this fixes screen tearing when using Optimus via PRIME (see
+        <option>hardware.nvidia.optimus_prime.enable</option>. This is not enabled
+        by default because it is not officially supported by NVIDIA and would not
+        work with SLI.
+      '';
+    };
+
+    hardware.nvidia.optimus_prime.enable = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME.
+        If enabled, the NVIDIA GPU will be always on and used for all rendering,
+        while enabling output to displays attached only to the integrated Intel GPU
+        without a multiplexer.
+
+        Note that this option only has any effect if the "nvidia" driver is specified
+        in <option>services.xserver.videoDrivers</option>, and it should preferably
+        be the only driver there.
+
+        If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
+        specified (<option>hardware.nvidia.optimus_prime.nvidiaBusId</option> and
+        <option>hardware.nvidia.optimus_prime.intelBusId</option>).
+
+        If you enable this, you may want to also enable kernel modesetting for the
+        NVIDIA driver (<option>hardware.nvidia.modesetting.enable</option>) in order
+        to prevent tearing.
+
+        Note that this configuration will only be successful when a display manager
+        for which the <option>services.xserver.displayManager.setupCommands</option>
+        option is supported is used; notably, SLiM is not supported.
+      '';
+    };
+
+    hardware.nvidia.optimus_prime.nvidiaBusId = lib.mkOption {
+      type = lib.types.string;
+      default = "";
+      example = "PCI:1:0:0";
+      description = ''
+        Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci
+        shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0".
+      '';
+    };
+
+    hardware.nvidia.optimus_prime.intelBusId = lib.mkOption {
+      type = lib.types.string;
+      default = "";
+      example = "PCI:0:2:0";
+      description = ''
+        Bus ID of the Intel GPU. You can find it using lspci; for example if lspci
+        shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0".
+      '';
+    };
+  };
 
   config = mkIf enabled {
     assertions = [
@@ -36,16 +100,62 @@ in
         assertion = config.services.xserver.displayManager.gdm.wayland;
         message = "NVidia drivers don't support wayland";
       }
+      {
+        assertion = !optimusCfg.enable ||
+          (optimusCfg.nvidiaBusId != "" && optimusCfg.intelBusId != "");
+        message = ''
+          When NVIDIA Optimus via PRIME is enabled, the GPU bus IDs must configured.
+        '';
+      }
     ];
 
-    services.xserver.drivers = singleton
-      { name = "nvidia"; modules = [ nvidia_x11.bin ]; libPath = [ nvidia_x11 ]; };
+    # If Optimus/PRIME is enabled, we:
+    # - Specify the configured NVIDIA GPU bus ID in the Device section for the
+    #   "nvidia" driver.
+    # - Add the AllowEmptyInitialConfiguration option to the Screen section for the
+    #   "nvidia" driver, in order to allow the X server to start without any outputs.
+    # - Add a separate Device section for the Intel GPU, using the "modesetting"
+    #   driver and with the configured BusID.
+    # - Reference that Device section from the ServerLayout section as an inactive
+    #   device.
+    # - Configure the display manager to run specific `xrandr` commands which will
+    #   configure/enable displays connected to the Intel GPU.
+
+    services.xserver.drivers = singleton {
+      name = "nvidia";
+      modules = [ nvidia_x11.bin ];
+      libPath = [ nvidia_x11 ];
+      deviceSection = optionalString optimusCfg.enable
+        ''
+          BusID "${optimusCfg.nvidiaBusId}"
+        '';
+      screenSection =
+        ''
+          Option "RandRRotation" "on"
+          ${optionalString optimusCfg.enable "Option \"AllowEmptyInitialConfiguration\""}
+        '';
+    };
 
-    services.xserver.screenSection =
+    services.xserver.extraConfig = optionalString optimusCfg.enable
+      ''
+        Section "Device"
+          Identifier "nvidia-optimus-intel"
+          Driver "modesetting"
+          BusID  "${optimusCfg.intelBusId}"
+          Option "AccelMethod" "none"
+        EndSection
+      '';
+    services.xserver.serverLayoutSection = optionalString optimusCfg.enable
       ''
-        Option "RandRRotation" "on"
+        Inactive "nvidia-optimus-intel"
       '';
 
+    services.xserver.displayManager.setupCommands = optionalString optimusCfg.enable ''
+      # Added by nvidia configuration module for Optimus/PRIME.
+      ${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource modesetting NVIDIA-0
+      ${pkgs.xorg.xrandr}/bin/xrandr --auto
+    '';
+
     environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
       source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
     };
@@ -62,6 +172,8 @@ in
     boot.kernelModules = [ "nvidia-uvm" ] ++
       lib.optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
 
+    # If requested enable modesetting via kernel parameter.
+    boot.kernelParams = optional cfg.modesetting.enable "nvidia-drm.modeset=1";
 
     # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
     services.udev.extraRules =
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index adde237c07c..1cfc8ff8612 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,6 +1,6 @@
 {
-  x86_64-linux = "/nix/store/mxg4bbblxfns96yrz0nalxyiyjl7gj98-nix-2.1.2";
-  i686-linux = "/nix/store/bgjgmbwirx63mwwychpikd7yc4k4lbjv-nix-2.1.2";
-  aarch64-linux = "/nix/store/yi18azn4nwrcwvaiag04jnxc1qs38fy5-nix-2.1.2";
-  x86_64-darwin = "/nix/store/fpivmcck2qpw5plrp599iraw2x9jp18k-nix-2.1.2";
+  x86_64-linux = "/nix/store/cdcia67siabmj6li7vyffgv2cry86fq8-nix-2.1.3";
+  i686-linux = "/nix/store/6q3xi6y5qnsv7d62b8n00hqfxi8rs2xs-nix-2.1.3";
+  aarch64-linux = "/nix/store/2v93d0vimlm28jg0ms6v1i6lc0fq13pn-nix-2.1.3";
+  x86_64-darwin = "/nix/store/dkjlfkrknmxbjmpfk3dg4q3nmb7m3zvk-nix-2.1.3";
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 359caad89a7..b70faa380e5 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -277,8 +277,7 @@ if ($virt eq "qemu" || $virt eq "kvm" || $virt eq "bochs") {
 
 # Also for Hyper-V.
 if ($virt eq "microsoft") {
-    push @initrdAvailableKernelModules, "hv_storvsc";
-    $videoDriver = "fbdev";
+    push @attrs, "virtualisation.hypervGuest.enable = true;"
 }
 
 
diff --git a/nixos/modules/installer/tools/nixos-option.sh b/nixos/modules/installer/tools/nixos-option.sh
index 3f1e591b97b..327e3e6989f 100644
--- a/nixos/modules/installer/tools/nixos-option.sh
+++ b/nixos/modules/installer/tools/nixos-option.sh
@@ -82,7 +82,7 @@ evalNix(){
   set -e
 
   if test $exit_code -eq 0; then
-      cat <<EOF
+      sed '/^warning: Nix search path/d' <<EOF
 $result
 EOF
       return 0;
@@ -90,7 +90,7 @@ EOF
       sed -n '
   /^error/ { s/, at (string):[0-9]*:[0-9]*//; p; };
   /^warning: Nix search path/ { p; };
-' <<EOF
+' >&2 <<EOF
 $result
 EOF
     exit_code=1
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index f4c7cf601bf..af630178a91 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -46,6 +46,7 @@
   ./hardware/opengl.nix
   ./hardware/pcmcia.nix
   ./hardware/raid/hpsa.nix
+  ./hardware/steam-hardware.nix
   ./hardware/usb-wwan.nix
   ./hardware/onlykey.nix
   ./hardware/video/amdgpu.nix
@@ -689,6 +690,7 @@
   ./services/web-apps/codimd.nix
   ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
+  ./services/web-apps/nextcloud.nix
   ./services/web-apps/nexus.nix
   ./services/web-apps/pgpkeyserver-lite.nix
   ./services/web-apps/matomo.nix
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index bef10b4fe61..926c6d77d3b 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -548,6 +548,13 @@ in
     environment.etc =
       mapAttrsToList (n: v: makePAMService v) config.security.pam.services;
 
+    systemd.tmpfiles.rules = optionals
+      (any (s: s.updateWtmp) (attrValues config.security.pam.services))
+      [
+        "f /var/log/wtmp"
+        "f /var/log/lastlog"
+      ];
+
     security.pam.services =
       { other.text =
           ''
diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix
index 04685f2c9ea..7f1de81d5b7 100644
--- a/nixos/modules/security/polkit.nix
+++ b/nixos/modules/security/polkit.nix
@@ -88,11 +88,11 @@ in
       "polkit-agent-helper-1".source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1";
     };
 
-    system.activationScripts.polkit =
-      ''
-        # Probably no more needed, clean up
-        rm -rf /var/lib/{polkit-1,PolicyKit}
-      '';
+    systemd.tmpfiles.rules = [
+      # Probably no more needed, clean up
+      "R /var/lib/polkit-1"
+      "R /var/lib/PolicyKit"
+    ];
 
     users.users.polkituser = {
       description = "PolKit daemon";
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index 392a8d5c2e7..46e3b745761 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -7,9 +7,10 @@ let
   cfg = config.services.bitlbee;
   bitlbeeUid = config.ids.uids.bitlbee;
 
-  bitlbeePkg = if cfg.libpurple_plugins == []
-  then pkgs.bitlbee
-  else pkgs.bitlbee.override { enableLibPurple = true; };
+  bitlbeePkg = pkgs.bitlbee.override {
+    enableLibPurple = cfg.libpurple_plugins != [];
+    enablePam = cfg.authBackend == "pam";
+  };
 
   bitlbeeConfig = pkgs.writeText "bitlbee.conf"
     ''
@@ -20,6 +21,7 @@ let
     DaemonInterface = ${cfg.interface}
     DaemonPort = ${toString cfg.portNumber}
     AuthMode = ${cfg.authMode}
+    AuthBackend = ${cfg.authBackend}
     Plugindir = ${pkgs.bitlbee-plugins cfg.plugins}/lib/bitlbee
     ${lib.optionalString (cfg.hostName != "") "HostName = ${cfg.hostName}"}
     ${lib.optionalString (cfg.protocols != "") "Protocols = ${cfg.protocols}"}
@@ -70,6 +72,16 @@ in
         '';
       };
 
+      authBackend = mkOption {
+        default = "storage";
+        type = types.enum [ "storage" "pam" ];
+        description = ''
+          How users are authenticated
+            storage -- save passwords internally
+            pam -- Linux PAM authentication
+        '';
+      };
+
       authMode = mkOption {
         default = "Open";
         type = types.enum [ "Open" "Closed" "Registered" ];
@@ -147,23 +159,22 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.bitlbee.enable {
-
-    users.users = singleton
-      { name = "bitlbee";
+  config =  mkMerge [
+    (mkIf config.services.bitlbee.enable {
+      users.users = singleton {
+        name = "bitlbee";
         uid = bitlbeeUid;
         description = "BitlBee user";
         home = "/var/lib/bitlbee";
         createHome = true;
       };
 
-    users.groups = singleton
-      { name = "bitlbee";
+      users.groups = singleton {
+        name = "bitlbee";
         gid = config.ids.gids.bitlbee;
       };
 
-    systemd.services.bitlbee =
-      {
+      systemd.services.bitlbee = {
         environment.PURPLE_PLUGIN_PATH = purple_plugin_path;
         description = "BitlBee IRC to other chat networks gateway";
         after = [ "network.target" ];
@@ -172,8 +183,12 @@ in
         serviceConfig.ExecStart = "${bitlbeePkg}/sbin/bitlbee -F -n -c ${bitlbeeConfig}";
       };
 
-    environment.systemPackages = [ bitlbeePkg ];
+      environment.systemPackages = [ bitlbeePkg ];
 
-  };
+    })
+    (mkIf (config.services.bitlbee.authBackend == "pam") {
+      security.pam.services.bitlbee = {};
+    })
+  ];
 
 }
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index 9ad0095252d..04b433f8f2b 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -95,7 +95,7 @@ in
     environment.etc."clamav/freshclam.conf".source = freshclamConfigFile;
     environment.etc."clamav/clamd.conf".source = clamdConfigFile;
 
-    systemd.services.clamav-daemon = optionalAttrs cfg.daemon.enable {
+    systemd.services.clamav-daemon = mkIf cfg.daemon.enable {
       description = "ClamAV daemon (clamd)";
       after = optional cfg.updater.enable "clamav-freshclam.service";
       requires = optional cfg.updater.enable "clamav-freshclam.service";
@@ -116,7 +116,7 @@ in
       };
     };
 
-    systemd.timers.clamav-freshclam = optionalAttrs cfg.updater.enable {
+    systemd.timers.clamav-freshclam = mkIf cfg.updater.enable {
       description = "Timer for ClamAV virus database updater (freshclam)";
       wantedBy = [ "timers.target" ];
       timerConfig = {
@@ -125,7 +125,7 @@ in
       };
     };
 
-    systemd.services.clamav-freshclam = optionalAttrs cfg.updater.enable {
+    systemd.services.clamav-freshclam = mkIf cfg.updater.enable {
       description = "ClamAV virus database updater (freshclam)";
       restartTriggers = [ freshclamConfigFile ];
 
@@ -137,6 +137,7 @@ in
       serviceConfig = {
         Type = "oneshot";
         ExecStart = "${pkg}/bin/freshclam";
+        SuccessExitStatus = "1"; # if databases are up to date
         PrivateTmp = "yes";
         PrivateDevices = "yes";
       };
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
new file mode 100644
index 00000000000..44c3df1d057
--- /dev/null
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -0,0 +1,463 @@
+{ config, lib, pkgs, ... }@args:
+
+with lib;
+
+let
+  cfg = config.services.nextcloud;
+
+  toKeyValue = generators.toKeyValue {
+    mkKeyValue = generators.mkKeyValueDefault {} " = ";
+  };
+
+  phpOptionsExtensions = ''
+    ${optionalString cfg.caching.apcu "extension=${cfg.phpPackages.apcu}/lib/php/extensions/apcu.so"}
+    ${optionalString cfg.caching.redis "extension=${cfg.phpPackages.redis}/lib/php/extensions/redis.so"}
+    ${optionalString cfg.caching.memcached "extension=${cfg.phpPackages.memcached}/lib/php/extensions/memcached.so"}
+    zend_extension = opcache.so
+    opcache.enable = 1
+  '';
+  phpOptions = {
+    upload_max_filesize = cfg.maxUploadSize;
+    post_max_size = cfg.maxUploadSize;
+    memory_limit = cfg.maxUploadSize;
+  } // cfg.phpOptions;
+  phpOptionsStr = phpOptionsExtensions + (toKeyValue phpOptions);
+
+  occ = pkgs.writeScriptBin "nextcloud-occ" ''
+    #! ${pkgs.stdenv.shell}
+    cd ${pkgs.nextcloud}
+    exec /run/wrappers/bin/sudo -u nextcloud \
+      NEXTCLOUD_CONFIG_DIR="${cfg.home}/config" \
+      ${config.services.phpfpm.phpPackage}/bin/php \
+      -c ${pkgs.writeText "php.ini" phpOptionsStr}\
+      occ $*
+  '';
+
+in {
+  options.services.nextcloud = {
+    enable = mkEnableOption "nextcloud";
+    hostName = mkOption {
+      type = types.str;
+      description = "FQDN for the nextcloud instance.";
+    };
+    home = mkOption {
+      type = types.str;
+      default = "/var/lib/nextcloud";
+      description = "Storage path of nextcloud.";
+    };
+    https = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable if there is a TLS terminating proxy in front of nextcloud.";
+    };
+
+    maxUploadSize = mkOption {
+      default = "512M";
+      type = types.str;
+      description = ''
+        Defines the upload limit for files. This changes the relevant options
+        in php.ini and nginx if enabled.
+      '';
+    };
+
+    skeletonDirectory = mkOption {
+      default = "";
+      type = types.str;
+      description = ''
+        The directory where the skeleton files are located. These files will be
+        copied to the data directory of new users. Leave empty to not copy any
+        skeleton files.
+      '';
+    };
+
+    nginx.enable = mkEnableOption "nginx vhost management";
+
+    webfinger = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable this option if you plan on using the webfinger plugin.
+        The appropriate nginx rewrite rules will be added to your configuration.
+      '';
+    };
+
+    phpPackages = mkOption {
+      type = types.attrs;
+      default = pkgs.php71Packages;
+      defaultText = "pkgs.php71Packages";
+      description = ''
+        Overridable attribute of the PHP packages set to use.  If any caching
+        module is enabled, it will be taken from here.  Therefore it should
+        match the version of PHP given to
+        <literal>services.phpfpm.phpPackage</literal>.
+      '';
+    };
+
+    phpOptions = mkOption {
+      type = types.attrsOf types.str;
+      default = {
+        "short_open_tag" = "Off";
+        "expose_php" = "Off";
+        "error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
+        "display_errors" = "stderr";
+        "opcache.enable_cli" = "1";
+        "opcache.interned_strings_buffer" = "8";
+        "opcache.max_accelerated_files" = "10000";
+        "opcache.memory_consumption" = "128";
+        "opcache.revalidate_freq" = "1";
+        "opcache.fast_shutdown" = "1";
+        "openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt";
+        "catch_workers_output" = "yes";
+      };
+      description = ''
+        Options for PHP's php.ini file for nextcloud.
+      '';
+    };
+
+    config = {
+      dbtype = mkOption {
+        type = types.enum [ "sqlite" "pgsql" "mysql" ];
+        default = "sqlite";
+        description = "Database type.";
+      };
+      dbname = mkOption {
+        type = types.nullOr types.str;
+        default = "nextcloud";
+        description = "Database name.";
+      };
+      dbuser = mkOption {
+        type = types.nullOr types.str;
+        default = "nextcloud";
+        description = "Database user.";
+      };
+      dbpass = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Database password.  Use <literal>dbpassFile</literal> to avoid this
+          being world-readable in the <literal>/nix/store</literal>.
+        '';
+      };
+      dbpassFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          The full path to a file that contains the database password.
+        '';
+      };
+      dbhost = mkOption {
+        type = types.nullOr types.str;
+        default = "localhost";
+        description = "Database host.";
+      };
+      dbport = mkOption {
+        type = with types; nullOr (either int str);
+        default = null;
+        description = "Database port.";
+      };
+      dbtableprefix = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "Table prefix in Nextcloud database.";
+      };
+      adminuser = mkOption {
+        type = types.str;
+        default = "root";
+        description = "Admin username.";
+      };
+      adminpass = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Database password.  Use <literal>adminpassFile</literal> to avoid this
+          being world-readable in the <literal>/nix/store</literal>.
+        '';
+      };
+      adminpassFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          The full path to a file that contains the admin's password.
+        '';
+      };
+
+      extraTrustedDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Trusted domains, from which the nextcloud installation will be
+          acessible.  You don't need to add
+          <literal>services.nextcloud.hostname</literal> here.
+        '';
+      };
+    };
+
+    caching = {
+      apcu = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to load the APCu module into PHP.
+        '';
+      };
+      redis = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to load the Redis module into PHP.
+          You still need to enable Redis in your config.php.
+          See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html
+        '';
+      };
+      memcached = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to load the Memcached module into PHP.
+          You still need to enable Memcached in your config.php.
+          See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable (mkMerge [
+    { assertions = let acfg = cfg.config; in [
+        { assertion = !(acfg.dbpass != null && acfg.dbpassFile != null);
+          message = "Please specify no more than one of dbpass or dbpassFile";
+        }
+        { assertion = ((acfg.adminpass != null || acfg.adminpassFile != null)
+            && !(acfg.adminpass != null && acfg.adminpassFile != null));
+          message = "Please specify exactly one of adminpass or adminpassFile";
+        }
+      ];
+    }
+
+    { systemd.timers."nextcloud-cron" = {
+        wantedBy = [ "timers.target" ];
+        timerConfig.OnBootSec = "5m";
+        timerConfig.OnUnitActiveSec = "15m";
+        timerConfig.Unit = "nextcloud-cron.service";
+      };
+
+      systemd.services = {
+        "nextcloud-setup" = let
+          overrideConfig = pkgs.writeText "nextcloud-config.php" ''
+            <?php
+            $CONFIG = [
+              'apps_paths' => [
+                [ 'path' => '${cfg.home}/apps', 'url' => '/apps', 'writable' => false ],
+                [ 'path' => '${cfg.home}/store-apps', 'url' => '/store-apps', 'writable' => true ],
+              ],
+              'datadirectory' => '${cfg.home}/data',
+              'skeletondirectory' => '${cfg.skeletonDirectory}',
+              ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"}
+              'log_type' => 'syslog',
+            ];
+          '';
+          occInstallCmd = let
+            c = cfg.config;
+            adminpass = if c.adminpassFile != null
+              then ''"$(<"${toString c.adminpassFile}")"''
+              else ''"${toString c.adminpass}"'';
+            dbpass = if c.dbpassFile != null
+              then ''"$(<"${toString c.dbpassFile}")"''
+              else if c.dbpass != null
+              then ''"${toString c.dbpass}"''
+              else null;
+            installFlags = concatStringsSep " \\\n    "
+              (mapAttrsToList (k: v: "${k} ${toString v}") {
+              "--database" = ''"${c.dbtype}"'';
+              # The following attributes are optional depending on the type of
+              # database.  Those that evaluate to null on the left hand side
+              # will be omitted.
+              ${if c.dbname != null then "--database-name" else null} = ''"${c.dbname}"'';
+              ${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"'';
+              ${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"'';
+              ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"'';
+              ${if (any (x: x != null) [c.dbpass c.dbpassFile])
+                 then "--database-pass" else null} = dbpass;
+              ${if c.dbtableprefix != null
+                then "--database-table-prefix" else null} = ''"${toString c.dbtableprefix}"'';
+              "--admin-user" = ''"${c.adminuser}"'';
+              "--admin-pass" = adminpass;
+              "--data-dir" = ''"${cfg.home}/data"'';
+            });
+          in ''
+            ${occ}/bin/nextcloud-occ maintenance:install \
+                ${installFlags}
+          '';
+          occSetTrustedDomainsCmd = concatStringsSep "\n" (imap0
+            (i: v: ''
+              ${occ}/bin/nextcloud-occ config:system:set trusted_domains \
+                ${toString i} --value="${toString v}"
+            '') ([ cfg.hostName ] ++ cfg.config.extraTrustedDomains));
+
+        in {
+          wantedBy = [ "multi-user.target" ];
+          before = [ "phpfpm-nextcloud.service" ];
+          script = ''
+            chmod og+x ${cfg.home}
+            ln -sf ${pkgs.nextcloud}/apps ${cfg.home}/
+            mkdir -p ${cfg.home}/config ${cfg.home}/data ${cfg.home}/store-apps
+            ln -sf ${overrideConfig} ${cfg.home}/config/override.config.php
+
+            chown -R nextcloud:nginx ${cfg.home}/config ${cfg.home}/data ${cfg.home}/store-apps
+
+            # Do not install if already installed
+            if [[ ! -e ${cfg.home}/config/config.php ]]; then
+              ${occInstallCmd}
+            fi
+
+            ${occ}/bin/nextcloud-occ upgrade
+
+            ${occ}/bin/nextcloud-occ config:system:delete trusted_domains
+            ${occSetTrustedDomainsCmd}
+          '';
+          serviceConfig.Type = "oneshot";
+        };
+        "nextcloud-cron" = {
+          environment.NEXTCLOUD_CONFIG_DIR = "${cfg.home}/config";
+          serviceConfig.Type = "oneshot";
+          serviceConfig.User = "nextcloud";
+          serviceConfig.ExecStart = "${pkgs.php}/bin/php -f ${pkgs.nextcloud}/cron.php";
+        };
+      };
+
+      services.phpfpm = {
+        phpOptions = phpOptionsExtensions;
+        phpPackage = pkgs.php71;
+        pools.nextcloud = let
+          phpAdminValues = (toKeyValue
+            (foldr (a: b: a // b) {}
+              (mapAttrsToList (k: v: { "php_admin_value[${k}]" = v; })
+                phpOptions)));
+        in {
+          listen = "/run/phpfpm/nextcloud";
+          extraConfig = ''
+            listen.owner = nginx
+            listen.group = nginx
+            user = nextcloud
+            group = nginx
+            pm = dynamic
+            pm.max_children = 32
+            pm.start_servers = 2
+            pm.min_spare_servers = 2
+            pm.max_spare_servers = 4
+            env[NEXTCLOUD_CONFIG_DIR] = ${cfg.home}/config
+            env[PATH] = /run/wrappers/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/usr/bin:/bin
+            ${phpAdminValues}
+          '';
+        };
+      };
+
+      users.extraUsers.nextcloud = {
+        home = "${cfg.home}";
+        group = "nginx";
+        createHome = true;
+      };
+
+      environment.systemPackages = [ occ ];
+    }
+
+    (mkIf cfg.nginx.enable {
+      services.nginx = {
+        enable = true;
+        virtualHosts = {
+          "${cfg.hostName}" = {
+            root = pkgs.nextcloud;
+            locations = {
+              "= /robots.txt" = {
+                priority = 100;
+                extraConfig = ''
+                  allow all;
+                  log_not_found off;
+                  access_log off;
+                '';
+              };
+              "/" = {
+                priority = 200;
+                extraConfig = "rewrite ^ /index.php$uri;";
+              };
+              "~ ^/store-apps" = {
+                priority = 201;
+                extraConfig = "root ${cfg.home};";
+              };
+              "= /.well-known/carddav" = {
+                priority = 210;
+                extraConfig = "return 301 $scheme://$host/remote.php/dav;";
+              };
+              "= /.well-known/caldav" = {
+                priority = 210;
+                extraConfig = "return 301 $scheme://$host/remote.php/dav;";
+              };
+              "~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/" = {
+                priority = 300;
+                extraConfig = "deny all;";
+              };
+              "~ ^/(?:\\.|autotest|occ|issue|indie|db_|console)" = {
+                priority = 300;
+                extraConfig = "deny all;";
+              };
+              "~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\\.php(?:$|/)" = {
+                priority = 500;
+                extraConfig = ''
+                  include ${pkgs.nginxMainline}/conf/fastcgi.conf;
+                  fastcgi_split_path_info ^(.+\.php)(/.*)$;
+                  fastcgi_param PATH_INFO $fastcgi_path_info;
+                  fastcgi_param HTTPS ${if cfg.https then "on" else "off"};
+                  fastcgi_param modHeadersAvailable true;
+                  fastcgi_param front_controller_active true;
+                  fastcgi_pass unix:/run/phpfpm/nextcloud;
+                  fastcgi_intercept_errors on;
+                  fastcgi_request_buffering off;
+                  fastcgi_read_timeout 120s;
+                '';
+              };
+              "~ ^/(?:updater|ocs-provider)(?:$|/)".extraConfig = ''
+                try_files $uri/ =404;
+                index index.php;
+              '';
+              "~ \\.(?:css|js|woff|svg|gif)$".extraConfig = ''
+                try_files $uri /index.php$uri$is_args$args;
+                add_header Cache-Control "public, max-age=15778463";
+                add_header X-Content-Type-Options nosniff;
+                add_header X-XSS-Protection "1; mode=block";
+                add_header X-Robots-Tag none;
+                add_header X-Download-Options noopen;
+                add_header X-Permitted-Cross-Domain-Policies none;
+                access_log off;
+              '';
+              "~ \\.(?:png|html|ttf|ico|jpg|jpeg)$".extraConfig = ''
+                try_files $uri /index.php$uri$is_args$args;
+                access_log off;
+              '';
+            };
+            extraConfig = ''
+              add_header X-Content-Type-Options nosniff;
+              add_header X-XSS-Protection "1; mode=block";
+              add_header X-Robots-Tag none;
+              add_header X-Download-Options noopen;
+              add_header X-Permitted-Cross-Domain-Policies none;
+              error_page 403 /core/templates/403.php;
+              error_page 404 /core/templates/404.php;
+              client_max_body_size ${cfg.maxUploadSize};
+              fastcgi_buffers 64 4K;
+              gzip on;
+              gzip_vary on;
+              gzip_comp_level 4;
+              gzip_min_length 256;
+              gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
+              gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
+
+              ${optionalString cfg.webfinger ''
+                rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
+                rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
+              ''}
+            '';
+          };
+        };
+      };
+    })
+  ]);
+}
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index b231ee5a3f0..508398f03ac 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -245,8 +245,8 @@ let
         }
       ''
   ) virtualHosts);
-  mkLocations = locations: concatStringsSep "\n" (mapAttrsToList (location: config: ''
-    location ${location} {
+  mkLocations = locations: concatStringsSep "\n" (map (config: ''
+    location ${config.location} {
       ${optionalString (config.proxyPass != null && !cfg.proxyResolveWhileRunning)
         "proxy_pass ${config.proxyPass};"
       }
@@ -266,7 +266,18 @@ let
       ${config.extraConfig}
       ${optionalString (config.proxyPass != null && cfg.recommendedProxySettings) "include ${recommendedProxyConfig};"}
     }
-  '') locations);
+  '') (sortProperties (mapAttrsToList (k: v: v // { location = k; }) locations)));
+  mkBasicAuth = vhostName: authDef: let
+    htpasswdFile = pkgs.writeText "${vhostName}.htpasswd" (
+      concatStringsSep "\n" (mapAttrsToList (user: password: ''
+        ${user}:{PLAIN}${password}
+      '') authDef)
+    );
+  in ''
+    auth_basic secured;
+    auth_basic_user_file ${htpasswdFile};
+  '';
+
   mkHtpasswd = vhostName: authDef: pkgs.writeText "${vhostName}.htpasswd" (
     concatStringsSep "\n" (mapAttrsToList (user: password: ''
       ${user}:{PLAIN}${password}
diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix
index 4c772734a74..9b44433d384 100644
--- a/nixos/modules/services/web-servers/nginx/location-options.nix
+++ b/nixos/modules/services/web-servers/nginx/location-options.nix
@@ -71,6 +71,16 @@ with lib;
         These lines go to the end of the location verbatim.
       '';
     };
+
+    priority = mkOption {
+      type = types.int;
+      default = 1000;
+      description = ''
+        Order of this location block in relation to the others in the vhost.
+        The semantics are the same as with `lib.mkOrder`. Smaller values have
+        a greater priority.
+      '';
+    };
   };
 }
 
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 70655592145..11c1aa4315a 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -64,7 +64,7 @@ in
       };
 
       security.wrappers = {
-        kcheckpass.source = "${lib.getBin plasma5.plasma-workspace}/lib/libexec/kcheckpass";
+        kcheckpass.source = "${lib.getBin plasma5.kscreenlocker}/lib/libexec/kcheckpass";
         "start_kdeinit".source = "${lib.getBin pkgs.kinit}/lib/libexec/kf5/start_kdeinit";
         kwin_wayland = {
           source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 357fa8ce8f3..26b79730dd3 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -222,6 +222,17 @@ in
         description = "List of arguments for the X server.";
       };
 
+      setupCommands = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Shell commands executed just after the X server has started.
+
+          This option is only effective for display managers for which this feature
+          is supported; currently these are LightDM, GDM and SDDM.
+        '';
+      };
+
       sessionCommands = mkOption {
         type = types.lines;
         default = "";
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index a775dd0f0e0..6cc30b218f4 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -7,6 +7,13 @@ let
   cfg = config.services.xserver.displayManager;
   gdm = pkgs.gnome3.gdm;
 
+  xSessionWrapper = if (cfg.setupCommands == "") then null else
+    pkgs.writeScript "gdm-x-session-wrapper" ''
+      #!${pkgs.bash}/bin/bash
+      ${cfg.setupCommands}
+      exec "$@"
+    '';
+
 in
 
 {
@@ -112,6 +119,11 @@ in
           GDM_SESSIONS_DIR = "${cfg.session.desktops}/share/xsessions";
           # Find the mouse
           XCURSOR_PATH = "~/.icons:${pkgs.gnome3.adwaita-icon-theme}/share/icons";
+        } // optionalAttrs (xSessionWrapper != null) {
+          # Make GDM use this wrapper before running the session, which runs the
+          # configured setupCommands. This relies on a patched GDM which supports
+          # this environment variable.
+          GDM_X_SESSION_WRAPPER = "${xSessionWrapper}";
         };
         execCmd = "exec ${gdm}/bin/gdm";
       };
@@ -142,7 +154,10 @@ in
 
     systemd.user.services.dbus.wantedBy = [ "default.target" ];
 
-    programs.dconf.profiles.gdm = "${gdm}/share/dconf/profile/gdm";
+    programs.dconf.profiles.gdm = pkgs.writeText "dconf-gdm-profile" ''
+      system-db:local
+      ${gdm}/share/dconf/profile/gdm
+    '';
 
     # Use AutomaticLogin if delay is zero, because it's immediate.
     # Otherwise with TimedLogin with zero seconds the prompt is still
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 8078b93a757..16f1ddea1a7 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -46,6 +46,7 @@ let
         greeters-directory = ${cfg.greeter.package}
       ''}
       sessions-directory = ${dmcfg.session.desktops}/share/xsessions
+      ${cfg.extraConfig}
 
       [Seat:*]
       xserver-command = ${xserverWrapper}
@@ -61,6 +62,12 @@ let
       ${optionalString hasDefaultUserSession ''
         user-session=${defaultSessionName}
       ''}
+      ${optionalString (dmcfg.setupCommands != "") ''
+        display-setup-script=${pkgs.writeScript "lightdm-display-setup" ''
+          #!${pkgs.bash}/bin/bash
+          ${dmcfg.setupCommands}
+        ''}
+      ''}
       ${cfg.extraSeatDefaults}
     '';
 
@@ -113,6 +120,15 @@ in
         };
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          user-authority-in-system-dir = true
+        '';
+        description = "Extra lines to append to LightDM section.";
+      };
+
       background = mkOption {
         type = types.str;
         default = "${pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom}/share/artwork/gnome/nix-wallpaper-simple-dark-gray_bottom.png";
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 2a982617773..522a0dc92d6 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -20,6 +20,7 @@ let
   Xsetup = pkgs.writeScript "Xsetup" ''
     #!/bin/sh
     ${cfg.setupScript}
+    ${dmcfg.setupCommands}
   '';
 
   Xstop = pkgs.writeScript "Xstop" ''
@@ -137,7 +138,8 @@ in
           xrandr --auto
         '';
         description = ''
-          A script to execute when starting the display server.
+          A script to execute when starting the display server. DEPRECATED, please
+          use <option>services.xserver.displayManager.setupCommands</option>.
         '';
       };
 
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 75bfeaac1fa..297e3631165 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -374,6 +374,12 @@ in
         description = "Contents of the first Monitor section of the X server configuration file.";
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional contents (sections) included in the X server configuration file";
+      };
+
       xrandrHeads = mkOption {
         default = [];
         example = [
@@ -754,6 +760,7 @@ in
             Driver "${driver.driverName or driver.name}"
             ${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""}
             ${cfg.deviceSection}
+            ${driver.deviceSection or ""}
             ${xrandrDeviceSection}
           EndSection
 
@@ -765,6 +772,7 @@ in
             ''}
 
             ${cfg.screenSection}
+            ${driver.screenSection or ""}
 
             ${optionalString (cfg.defaultDepth != 0) ''
               DefaultDepth ${toString cfg.defaultDepth}
@@ -794,6 +802,8 @@ in
         '')}
 
         ${xrandrMonitorSections}
+
+        ${cfg.extraConfig}
       '';
 
     fonts.enableDefaultFonts = mkDefault true;
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 5a87a2ec762..b1eaf018956 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -174,14 +174,6 @@ in
       ''
         # Various log/runtime directories.
 
-        mkdir -m 0755 -p /run/nix/current-load # for distributed builds
-        mkdir -m 0700 -p /run/nix/remote-stores
-
-        mkdir -m 0755 -p /var/log
-
-        touch /var/log/wtmp /var/log/lastlog # must exist
-        chmod 644 /var/log/wtmp /var/log/lastlog
-
         mkdir -m 1777 -p /var/tmp
 
         # Empty, immutable home directory of many system accounts.
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index a9824649e38..397b308b731 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -419,8 +419,8 @@ while (my $f = <$listActiveUsers>) {
     my ($uid, $name) = ($+{uid}, $+{user});
     print STDERR "reloading user units for $name...\n";
 
-    system("su", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user daemon-reload");
-    system("su", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user start nixos-activation.service");
+    system("@su@", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user daemon-reload");
+    system("@su@", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user start nixos-activation.service");
 }
 
 close $listActiveUsers;
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 413543df88c..a560af5ce96 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -109,6 +109,7 @@ let
     inherit (pkgs) utillinux coreutils;
     systemd = config.systemd.package;
     shell = "${pkgs.bash}/bin/sh";
+    su = "${pkgs.shadow.su}/bin/su";
 
     inherit children;
     kernelParams = config.boot.kernelParams;
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index 49764b75a55..03daafa1ce4 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -152,6 +152,14 @@ ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system
 @shell@ @postBootCommands@
 
 
+# Ensure systemd doesn't try to populate /etc, by forcing its first-boot
+# heuristic off. It doesn't matter what's in /etc/machine-id for this purpose,
+# and systemd will immediately fill in the file when it starts, so just
+# creating it is enough. This `: >>` pattern avoids forking and avoids changing
+# the mtime if the file already exists.
+: >> /etc/machine-id
+
+
 # Reset the logging file descriptors.
 exec 1>&$logOutFd 2>&$logErrFd
 exec {logOutFd}>&- {logErrFd}>&-
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index 2cff25a8c85..5f2bec5c34a 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -394,7 +394,7 @@ in rec {
         Each attribute in this set specifies an option in the
         <literal>[Timer]</literal> section of the unit.  See
         <citerefentry><refentrytitle>systemd.timer</refentrytitle>
-        <manvolnum>7</manvolnum></citerefentry> and
+        <manvolnum>5</manvolnum></citerefentry> and
         <citerefentry><refentrytitle>systemd.time</refentrytitle>
         <manvolnum>7</manvolnum></citerefentry> for details.
       '';
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 3ac4c02b61f..a1412bc3290 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -747,6 +747,7 @@ in
 
       "systemd/journald.conf".text = ''
         [Journal]
+        Storage=persistent
         RateLimitInterval=${config.services.journald.rateLimitInterval}
         RateLimitBurst=${toString config.services.journald.rateLimitBurst}
         ${optionalString (config.services.journald.console != "") ''
@@ -783,19 +784,6 @@ in
 
     services.dbus.enable = true;
 
-    system.activationScripts.systemd = stringAfter [ "groups" ]
-      ''
-        mkdir -m 0755 -p /var/lib/udev
-
-        if ! [ -e /etc/machine-id ]; then
-          ${systemd}/bin/systemd-machine-id-setup
-        fi
-
-        # Keep a persistent journal. Note that systemd-tmpfiles will
-        # set proper ownership/permissions.
-        mkdir -m 0700 -p /var/log/journal
-      '';
-
     users.users.systemd-network.uid = config.ids.uids.systemd-network;
     users.groups.systemd-network.gid = config.ids.gids.systemd-network;
     users.users.systemd-resolve.uid = config.ids.uids.systemd-resolve;
diff --git a/nixos/modules/virtualisation/hyperv-guest.nix b/nixos/modules/virtualisation/hyperv-guest.nix
index ecd2a811771..0f1f052880c 100644
--- a/nixos/modules/virtualisation/hyperv-guest.nix
+++ b/nixos/modules/virtualisation/hyperv-guest.nix
@@ -9,20 +9,47 @@ in {
   options = {
     virtualisation.hypervGuest = {
       enable = mkEnableOption "Hyper-V Guest Support";
+
+      videoMode = mkOption {
+        type = types.str;
+        default = "1152x864";
+        example = "1024x768";
+        description = ''
+          Resolution at which to initialize the video adapter.
+
+          Supports screen resolution up to Full HD 1920x1080 with 32 bit color
+          on Windows Server 2012, and 1600x1200 with 16 bit color on Windows
+          Server 2008 R2 or earlier.
+        '';
+      };
     };
   };
 
   config = mkIf cfg.enable {
+    boot = {
+      initrd.kernelModules = [
+        "hv_balloon" "hv_netvsc" "hv_storvsc" "hv_utils" "hv_vmbus"
+      ];
+
+      kernelParams = [
+        "video=hyperv_fb:${cfg.videoMode}"
+      ];
+    };
+
     environment.systemPackages = [ config.boot.kernelPackages.hyperv-daemons.bin ];
 
     security.rngd.enable = false;
 
-    # enable hotadding memory
+    # enable hotadding cpu/memory
     services.udev.packages = lib.singleton (pkgs.writeTextFile {
-      name = "hyperv-memory-hotadd-udev-rules";
-      destination = "/etc/udev/rules.d/99-hyperv-memory-hotadd.rules";
+      name = "hyperv-cpu-and-memory-hotadd-udev-rules";
+      destination = "/etc/udev/rules.d/99-hyperv-cpu-and-memory-hotadd.rules";
       text = ''
-        ACTION="add", SUBSYSTEM=="memory", ATTR{state}="online"
+        # Memory hotadd
+        SUBSYSTEM=="memory", ACTION=="add", DEVPATH=="/devices/system/memory/memory[0-9]*", TEST=="state", ATTR{state}="online"
+
+        # CPU hotadd
+        SUBSYSTEM=="cpu", ACTION=="add", DEVPATH=="/devices/system/cpu/cpu[0-9]*", TEST=="online", ATTR{online}="1"
       '';
     });
 
diff --git a/nixos/release.nix b/nixos/release.nix
index e53ebff9b6d..66dbf697c8a 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -362,6 +362,7 @@ in rec {
   tests.netdata = callTest tests/netdata.nix { };
   tests.networking.networkd = callSubTests tests/networking.nix { networkd = true; };
   tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; };
+  tests.nextcloud = callSubTests tests/nextcloud { };
   # TODO: put in networking.nix after the test becomes more complete
   tests.networkingProxy = callTest tests/networking-proxy.nix {};
   tests.nexus = callTest tests/nexus.nix { };
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index 6d72ac997f8..3ad55651b11 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -78,6 +78,8 @@ import ./make-test.nix ({ pkgs, ...} : rec {
 
       # Test whether we have a reboot record in wtmp.
       subtest "reboot-wtmp", sub {
+          $machine->shutdown;
+          $machine->waitForUnit('multi-user.target');
           $machine->succeed("last | grep reboot >&2");
       };
 
diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix
new file mode 100644
index 00000000000..c3b710f0f90
--- /dev/null
+++ b/nixos/tests/nextcloud/basic.nix
@@ -0,0 +1,56 @@
+import ../make-test.nix ({ pkgs, ...}: let
+  adminpass = "notproduction";
+  adminuser = "root";
+in {
+  name = "nextcloud-basic";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ globin eqyiel ];
+  };
+
+  nodes = {
+    # The only thing the client needs to do is download a file.
+    client = { ... }: {};
+
+    nextcloud = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 80 ];
+
+      services.nextcloud = {
+        enable = true;
+        nginx.enable = true;
+        hostName = "nextcloud";
+        config = {
+          # Don't inherit adminuser since "root" is supposed to be the default
+          inherit adminpass;
+        };
+      };
+    };
+  };
+
+  testScript = let
+    withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
+      #!${pkgs.stdenv.shell}
+      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
+      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
+      "''${@}"
+    '';
+    copySharedFile = pkgs.writeScript "copy-shared-file" ''
+      #!${pkgs.stdenv.shell}
+      echo 'hi' | ${withRcloneEnv} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
+    '';
+
+    diffSharedFile = pkgs.writeScript "diff-shared-file" ''
+      #!${pkgs.stdenv.shell}
+      diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file)
+    '';
+  in ''
+    startAll();
+    $nextcloud->waitForUnit("multi-user.target");
+    $nextcloud->succeed("curl -sSf http://nextcloud/login");
+    $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}");
+    $client->waitForUnit("multi-user.target");
+    $client->succeed("${withRcloneEnv} ${diffSharedFile}");
+  '';
+})
diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix
new file mode 100644
index 00000000000..66da6794b96
--- /dev/null
+++ b/nixos/tests/nextcloud/default.nix
@@ -0,0 +1,6 @@
+{ system ? builtins.currentSystem }:
+{
+  basic = import ./basic.nix { inherit system; };
+  with-postgresql-and-redis = import ./with-postgresql-and-redis.nix { inherit system; };
+  with-mysql-and-memcached = import ./with-mysql-and-memcached.nix { inherit system; };
+}
diff --git a/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
new file mode 100644
index 00000000000..c0d347238b4
--- /dev/null
+++ b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
@@ -0,0 +1,97 @@
+import ../make-test.nix ({ pkgs, ...}: let
+  adminpass = "hunter2";
+  adminuser = "root";
+in {
+  name = "nextcloud-with-mysql-and-memcached";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ eqyiel ];
+  };
+
+  nodes = {
+    # The only thing the client needs to do is download a file.
+    client = { ... }: {};
+
+    nextcloud = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 80 ];
+
+      services.nextcloud = {
+        enable = true;
+        hostName = "nextcloud";
+        nginx.enable = true;
+        https = true;
+        caching = {
+          apcu = true;
+          redis = false;
+          memcached = true;
+        };
+        config = {
+          dbtype = "mysql";
+          dbname = "nextcloud";
+          dbuser = "nextcloud";
+          dbhost = "127.0.0.1";
+          dbport = 3306;
+          dbpass = "hunter2";
+          # Don't inherit adminuser since "root" is supposed to be the default
+          inherit adminpass;
+        };
+      };
+
+      services.mysql = {
+        enable = true;
+        bind = "127.0.0.1";
+        package = pkgs.mariadb;
+        initialScript = pkgs.writeText "mysql-init" ''
+          CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'hunter2';
+          CREATE DATABASE IF NOT EXISTS nextcloud;
+          GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
+            CREATE TEMPORARY TABLES ON nextcloud.* TO 'nextcloud'@'localhost'
+            IDENTIFIED BY 'hunter2';
+          FLUSH privileges;
+        '';
+      };
+
+      systemd.services."nextcloud-setup"= {
+        requires = ["mysql.service"];
+        after = ["mysql.service"];
+      };
+
+      services.memcached.enable = true;
+    };
+  };
+
+  testScript = let
+    configureMemcached = pkgs.writeScript "configure-memcached" ''
+      #!${pkgs.stdenv.shell}
+      nextcloud-occ config:system:set memcached_servers 0 0 --value 127.0.0.1 --type string
+      nextcloud-occ config:system:set memcached_servers 0 1 --value 11211 --type integer
+      nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\APCu' --type string
+      nextcloud-occ config:system:set memcache.distributed --value '\OC\Memcache\Memcached' --type string
+    '';
+    withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
+      #!${pkgs.stdenv.shell}
+      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
+      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
+    '';
+    copySharedFile = pkgs.writeScript "copy-shared-file" ''
+      #!${pkgs.stdenv.shell}
+      echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
+    '';
+
+    diffSharedFile = pkgs.writeScript "diff-shared-file" ''
+      #!${pkgs.stdenv.shell}
+      diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file)
+    '';
+  in ''
+    startAll();
+    $nextcloud->waitForUnit("multi-user.target");
+    $nextcloud->succeed("${configureMemcached}");
+    $nextcloud->succeed("curl -sSf http://nextcloud/login");
+    $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}");
+    $client->waitForUnit("multi-user.target");
+    $client->succeed("${withRcloneEnv} ${diffSharedFile}");
+
+  '';
+})
diff --git a/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
new file mode 100644
index 00000000000..0351d4db69a
--- /dev/null
+++ b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
@@ -0,0 +1,130 @@
+import ../make-test.nix ({ pkgs, ...}: let
+  adminpass = "hunter2";
+  adminuser = "custom-admin-username";
+in {
+  name = "nextcloud-with-postgresql-and-redis";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ eqyiel ];
+  };
+
+  nodes = {
+    # The only thing the client needs to do is download a file.
+    client = { ... }: {};
+
+    nextcloud = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 80 ];
+
+      services.nextcloud = {
+        enable = true;
+        hostName = "nextcloud";
+        nginx.enable = true;
+        caching = {
+          apcu = false;
+          redis = true;
+          memcached = false;
+        };
+        config = {
+          dbtype = "pgsql";
+          dbname = "nextcloud";
+          dbuser = "nextcloud";
+          dbhost = "localhost";
+          dbpassFile = toString (pkgs.writeText "db-pass-file" ''
+            hunter2
+          '');
+          inherit adminuser;
+          adminpassFile = toString (pkgs.writeText "admin-pass-file" ''
+            ${adminpass}
+          '');
+        };
+      };
+
+      services.redis = {
+        unixSocket = "/var/run/redis/redis.sock";
+        enable = true;
+        extraConfig = ''
+          unixsocketperm 770
+        '';
+      };
+
+      systemd.services.redis = {
+        preStart = ''
+          mkdir -p /var/run/redis
+          chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis
+        '';
+        serviceConfig.PermissionsStartOnly = true;
+      };
+
+      systemd.services."nextcloud-setup"= {
+        requires = ["postgresql.service"];
+        after = [
+          "postgresql.service"
+          "chown-redis-socket.service"
+        ];
+      };
+
+      # At the time of writing, redis creates its socket with the "nobody"
+      # group.  I figure this is slightly less bad than making the socket world
+      # readable.
+      systemd.services."chown-redis-socket" = {
+        enable = true;
+        script = ''
+          until ${pkgs.redis}/bin/redis-cli ping; do
+            echo "waiting for redis..."
+            sleep 1
+          done
+          chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis/redis.sock
+        '';
+        after = [ "redis.service" ];
+        requires = [ "redis.service" ];
+        wantedBy = [ "redis.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+        };
+      };
+
+      services.postgresql = {
+        enable = true;
+        initialScript = pkgs.writeText "psql-init" ''
+          create role nextcloud with login password 'hunter2';
+          create database nextcloud with owner nextcloud;
+        '';
+      };
+    };
+  };
+
+  testScript = let
+    configureRedis = pkgs.writeScript "configure-redis" ''
+      #!${pkgs.stdenv.shell}
+      nextcloud-occ config:system:set redis 'host' --value '/var/run/redis/redis.sock' --type string
+      nextcloud-occ config:system:set redis 'port' --value 0 --type integer
+      nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string
+      nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string
+    '';
+    withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
+      #!${pkgs.stdenv.shell}
+      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
+      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
+      "''${@}"
+    '';
+    copySharedFile = pkgs.writeScript "copy-shared-file" ''
+      #!${pkgs.stdenv.shell}
+      echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
+    '';
+
+    diffSharedFile = pkgs.writeScript "diff-shared-file" ''
+      #!${pkgs.stdenv.shell}
+      diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file)
+    '';
+  in ''
+    startAll();
+    $nextcloud->waitForUnit("multi-user.target");
+    $nextcloud->succeed("${configureRedis}");
+    $nextcloud->succeed("curl -sSf http://nextcloud/login");
+    $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}");
+    $client->waitForUnit("multi-user.target");
+    $client->succeed("${withRcloneEnv} ${diffSharedFile}");
+  '';
+})
diff --git a/nixos/tests/nix-ssh-serve.nix b/nixos/tests/nix-ssh-serve.nix
index aa366d8612d..494d55121eb 100644
--- a/nixos/tests/nix-ssh-serve.nix
+++ b/nixos/tests/nix-ssh-serve.nix
@@ -14,8 +14,8 @@ in
              keys = [ snakeOilPublicKey ];
              protocol = "ssh-ng";
            };
-         server.nix.package = pkgs.nixUnstable;
-         client.nix.package = pkgs.nixUnstable;
+         server.nix.package = pkgs.nix;
+         client.nix.package = pkgs.nix;
        };
      testScript = ''
        startAll;