summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/installation/installing.xml2
-rw-r--r--nixos/doc/manual/man-nixos-version.xml97
-rw-r--r--nixos/doc/manual/man-pages.xml1
-rw-r--r--nixos/lib/testing.nix8
-rw-r--r--nixos/lib/utils.nix8
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh121
-rw-r--r--nixos/modules/config/fonts/fontconfig-ultimate.nix153
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix262
-rw-r--r--nixos/modules/config/i18n.nix9
-rw-r--r--nixos/modules/config/shells-environment.nix8
-rw-r--r--nixos/modules/config/swap.nix9
-rw-r--r--nixos/modules/config/update-users-groups.pl4
-rw-r--r--nixos/modules/config/users-groups.nix35
-rw-r--r--nixos/modules/i18n/input-method/fcitx.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix25
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix1
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh10
-rw-r--r--nixos/modules/installer/tools/nixos-version.sh4
-rw-r--r--nixos/modules/installer/tools/tools.nix1
-rw-r--r--nixos/modules/misc/ids.nix8
-rw-r--r--nixos/modules/module-list.nix16
-rw-r--r--nixos/modules/profiles/installation-device.nix2
-rw-r--r--nixos/modules/programs/bash/bash.nix2
-rw-r--r--nixos/modules/programs/bash/inputrc1
-rw-r--r--nixos/modules/programs/shadow.nix18
-rw-r--r--nixos/modules/programs/spacefm.nix55
-rw-r--r--nixos/modules/rename.nix20
-rw-r--r--nixos/modules/security/acme.nix2
-rw-r--r--nixos/modules/security/grsecurity.nix376
-rw-r--r--nixos/modules/services/audio/squeezelite.nix67
-rw-r--r--nixos/modules/services/continuous-integration/buildkite-agent.nix100
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix2
-rw-r--r--nixos/modules/services/databases/openldap.nix11
-rw-r--r--nixos/modules/services/editors/emacs.nix86
-rw-r--r--nixos/modules/services/games/factorio.nix4
-rw-r--r--nixos/modules/services/hardware/tlp.nix4
-rw-r--r--nixos/modules/services/logging/logstash.nix2
-rw-r--r--nixos/modules/services/mail/opendkim.nix2
-rw-r--r--nixos/modules/services/mail/postfix.nix32
-rw-r--r--nixos/modules/services/misc/confd.nix2
-rw-r--r--nixos/modules/services/misc/disnix.nix108
-rw-r--r--nixos/modules/services/misc/dysnomia.nix217
-rw-r--r--nixos/modules/services/misc/etcd.nix2
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix324
-rw-r--r--nixos/modules/services/misc/parsoid.nix2
-rw-r--r--nixos/modules/services/misc/taskserver/default.nix2
-rw-r--r--nixos/modules/services/monitoring/bosun.nix2
-rw-r--r--nixos/modules/services/monitoring/grafana.nix2
-rw-r--r--nixos/modules/services/monitoring/scollector.nix2
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix9
-rw-r--r--nixos/modules/services/networking/consul.nix8
-rw-r--r--nixos/modules/services/networking/coturn.nix327
-rw-r--r--nixos/modules/services/networking/ddclient.nix36
-rw-r--r--nixos/modules/services/networking/ejabberd.nix2
-rw-r--r--nixos/modules/services/networking/networkmanager.nix5
-rw-r--r--nixos/modules/services/networking/offlineimap.nix73
-rw-r--r--nixos/modules/services/networking/pptpd.nix124
-rw-r--r--nixos/modules/services/networking/shairport-sync.nix2
-rw-r--r--nixos/modules/services/networking/skydns.nix2
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix2
-rw-r--r--nixos/modules/services/networking/xl2tpd.nix143
-rw-r--r--nixos/modules/services/search/elasticsearch.nix13
-rw-r--r--nixos/modules/services/security/hologram.nix2
-rw-r--r--nixos/modules/services/security/oauth2_proxy.nix2
-rw-r--r--nixos/modules/services/system/dbus.nix9
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix23
-rw-r--r--nixos/modules/services/web-servers/caddy.nix2
-rw-r--r--nixos/modules/services/web-servers/lighttpd/inginious.nix262
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix7
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix10
-rw-r--r--nixos/modules/services/x11/window-managers/i3.nix71
-rw-r--r--nixos/modules/services/x11/xbanish.nix30
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl2
-rw-r--r--nixos/modules/system/boot/coredump.nix4
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix3
-rw-r--r--nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix1
-rw-r--r--nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh9
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl4
-rw-r--r--nixos/modules/system/boot/luksroot.nix26
-rw-r--r--nixos/modules/system/boot/plymouth.nix129
-rw-r--r--nixos/modules/system/boot/resolved.nix67
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh6
-rw-r--r--nixos/modules/system/boot/stage-1.nix26
-rw-r--r--nixos/modules/system/boot/systemd.nix5
-rw-r--r--nixos/modules/tasks/filesystems.nix20
-rw-r--r--nixos/modules/tasks/kbd.nix102
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix5
-rw-r--r--nixos/modules/virtualisation/docker.nix2
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix80
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix260
-rw-r--r--nixos/modules/virtualisation/lxd.nix2
-rw-r--r--nixos/release-combined.nix1
-rw-r--r--nixos/release.nix1
-rw-r--r--nixos/tests/ecryptfs.nix81
-rw-r--r--nixos/tests/emacs-daemon.nix45
-rw-r--r--nixos/tests/gnome3_18-gdm.nix (renamed from nixos/tests/gnome3_20-gdm.nix)0
-rw-r--r--nixos/tests/gnome3_18.nix (renamed from nixos/tests/gnome3_20.nix)2
-rw-r--r--nixos/tests/grsecurity.nix40
98 files changed, 3281 insertions, 1039 deletions
diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml
index 2f118d27b1a5..073f7ca34620 100644
--- a/nixos/doc/manual/installation/installing.xml
+++ b/nixos/doc/manual/installation/installing.xml
@@ -22,7 +22,7 @@
   (with empty password).</para></listitem>
 
   <listitem><para>If you downloaded the graphical ISO image, you can
-  run <command>start display-manager</command> to start KDE. If you
+  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>
diff --git a/nixos/doc/manual/man-nixos-version.xml b/nixos/doc/manual/man-nixos-version.xml
new file mode 100644
index 000000000000..615d74f90908
--- /dev/null
+++ b/nixos/doc/manual/man-nixos-version.xml
@@ -0,0 +1,97 @@
+<refentry xmlns="http://docbook.org/ns/docbook"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<refmeta>
+  <refentrytitle><command>nixos-version</command></refentrytitle>
+  <manvolnum>8</manvolnum>
+  <refmiscinfo class="source">NixOS</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+  <refname><command>nixos-version</command></refname>
+  <refpurpose>show the NixOS version</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+  <cmdsynopsis>
+    <command>nixos-version</command>
+    <arg><option>--hash</option></arg>
+    <arg><option>--revision</option></arg>
+  </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsection><title>Description</title>
+
+<para>This command shows the version of the currently active NixOS
+configuration. For example:
+
+<screen>$ nixos-version
+16.03.1011.6317da4 (Emu)
+</screen>
+
+The version consists of the following elements:
+
+<variablelist>
+
+  <varlistentry>
+    <term><literal>16.03</literal></term>
+    <listitem><para>The NixOS release, indicating the year and month
+    in which it was released (e.g. March 2016).</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><literal>1011</literal></term>
+    <listitem><para>The number of commits in the Nixpkgs Git
+    repository between the start of the release branch and the commit
+    from which this version was built. This ensures that NixOS
+    versions are monotonically increasing. It is
+    <literal>git</literal> when the current NixOS configuration was
+    built from a checkout of the Nixpkgs Git repository rather than
+    from a NixOS channel.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><literal>6317da4</literal></term>
+    <listitem><para>The first 7 characters of the commit in the
+    Nixpkgs Git repository from which this version was
+    built.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><literal>Emu</literal></term>
+    <listitem><para>The code name of the NixOS release. The first
+    letter of the code name indicates that this is the N'th stable
+    NixOS release; for example, Emu is the fifth
+    release.</para></listitem>
+  </varlistentry>
+
+</variablelist>
+
+</para>
+
+</refsection>
+
+
+<refsection><title>Options</title>
+
+<para>This command accepts the following options:</para>
+
+<variablelist>
+
+  <varlistentry>
+    <term><option>--hash</option></term>
+    <term><option>--revision</option></term>
+    <listitem>
+      <para>Show the full SHA1 hash of the Git commit from which this
+      configuration was built, e.g.
+<screen>$ nixos-version --hash
+6317da40006f6bc2480c6781999c52d88dde2acf
+</screen>
+      </para>
+    </listitem>
+  </varlistentry>
+</variablelist>
+
+</refsection>
+</refentry>
diff --git a/nixos/doc/manual/man-pages.xml b/nixos/doc/manual/man-pages.xml
index 97a2c16d406e..e945e0e62639 100644
--- a/nixos/doc/manual/man-pages.xml
+++ b/nixos/doc/manual/man-pages.xml
@@ -27,5 +27,6 @@
   <xi:include href="man-nixos-install.xml" />
   <xi:include href="man-nixos-option.xml" />
   <xi:include href="man-nixos-rebuild.xml" />
+  <xi:include href="man-nixos-version.xml" />
 
 </reference>
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index f7159634e4f1..5f463c092846 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -113,14 +113,14 @@ rec {
             --add-flags "$vms" \
             ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
             --run "testScript=\"\$(cat $out/test-script)\"" \
-            --set testScript '"$testScript"' \
-            --set VLANS '"${toString vlans}"'
+            --set testScript '$testScript' \
+            --set VLANS '${toString vlans}'
           ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
           wrapProgram $out/bin/nixos-run-vms \
             --add-flags "$vms" \
             ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
-            --set tests '"startAll; joinAll;"' \
-            --set VLANS '"${toString vlans}"' \
+            --set tests 'startAll; joinAll;' \
+            --set VLANS '${toString vlans}' \
             ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
         ''; # "
 
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index 7b8be2050c13..40d0854d968d 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -8,4 +8,12 @@ rec {
    replaceChars ["/" "-" " "] ["-" "\\x2d" "\\x20"]
     (if hasPrefix "/" s then substring 1 (stringLength s) s else s);
 
+  # Returns a system path for a given shell package
+  toShellPath = shell:
+    if types.shellPackage.check shell then
+      "/run/current-system/sw${shell.shellPath}"
+    else if types.package.check shell then
+      throw "${shell} is not a shell package"
+    else
+      shell;
 }
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index 192f608e138f..e26caa191643 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -13,8 +13,11 @@ echo "NixOS version is $version ($major)"
 
 rm -f ec2-amis.nix
 
+types="hvm pv"
+stores="ebs s3"
+regions="eu-west-1 eu-central-1 us-east-1 us-west-1 us-west-2 ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2 sa-east-1 ap-south-1"
 
-for type in hvm pv; do
+for type in $types; do
     link=$stateDir/$type
     imageFile=$link/nixos.qcow2
     system=x86_64-linux
@@ -31,7 +34,7 @@ for type in hvm pv; do
             --arg configuration "{ imports = [ <nixpkgs/nixos/maintainers/scripts/ec2/amazon-image.nix> ]; ec2.hvm = $hvmFlag; }"
     fi
 
-    for store in ebs s3; do
+    for store in $stores; do
 
         bucket=nixos-amis
         bucketDir="$version-$type-$store"
@@ -39,7 +42,7 @@ for type in hvm pv; do
         prevAmi=
         prevRegion=
 
-        for region in eu-west-1 eu-central-1 us-east-1 us-west-1 us-west-2 ap-southeast-1 ap-southeast-2 ap-northeast-1 sa-east-1; do
+        for region in $regions; do
 
             name=nixos-$version-$arch-$type-$store
             description="NixOS $system $version ($type-$store)"
@@ -51,10 +54,11 @@ for type in hvm pv; do
                 echo "doing $name in $region..."
 
                 if [ -n "$prevAmi" ]; then
-                    ami=$(ec2-copy-image \
+                    ami=$(aws ec2 copy-image \
                         --region "$region" \
-                        --source-region "$prevRegion" --source-ami-id "$prevAmi" \
-                        --name "$name" --description "$description" | cut -f 2)
+                        --source-region "$prevRegion" --source-image-id "$prevAmi" \
+                        --name "$name" --description "$description" | json -q .ImageId)
+                    if [ "$ami" = null ]; then break; fi
                 else
 
                     if [ $store = s3 ]; then
@@ -85,12 +89,12 @@ for type in hvm pv; do
                             ec2-upload-bundle \
                                 -m $imageDir/$type.raw.manifest.xml \
                                 -b "$bucket/$bucketDir" \
-                                -a "$EC2_ACCESS_KEY" -s "$EC2_SECRET_KEY" \
+                                -a "$AWS_ACCESS_KEY_ID" -s "$AWS_SECRET_ACCESS_KEY" \
                                 --location EU
                             touch $imageDir/uploaded
                         fi
 
-                        extraFlags="$bucket/$bucketDir/$type.raw.manifest.xml"
+                        extraFlags="--image-location $bucket/$bucketDir/$type.raw.manifest.xml"
 
                     else
 
@@ -115,7 +119,8 @@ for type in hvm pv; do
                         if [ -z "$snapId" -a -z "$volId" -a -z "$taskId" ]; then
                             echo "importing $vhdFile..."
                             taskId=$(ec2-import-volume $vhdFile --no-upload -f vhd \
-                                -o "$EC2_ACCESS_KEY" -w "$EC2_SECRET_KEY" \
+                                -O "$AWS_ACCESS_KEY_ID" -W "$AWS_SECRET_ACCESS_KEY" \
+                                -o "$AWS_ACCESS_KEY_ID" -w "$AWS_SECRET_ACCESS_KEY" \
                                 --region "$region" -z "${region}a" \
                                 --bucket "$bucket" --prefix "$bucketDir/" \
                                 | tee /dev/stderr \
@@ -125,15 +130,16 @@ for type in hvm pv; do
 
                         if [ -z "$snapId" -a -z "$volId" ]; then
                             ec2-resume-import  $vhdFile -t "$taskId" --region "$region" \
-                                -o "$EC2_ACCESS_KEY" -w "$EC2_SECRET_KEY"
+                                -O "$AWS_ACCESS_KEY_ID" -W "$AWS_SECRET_ACCESS_KEY" \
+                                -o "$AWS_ACCESS_KEY_ID" -w "$AWS_SECRET_ACCESS_KEY"
                         fi
 
                         # Wait for the volume creation to finish.
                         if [ -z "$snapId" -a -z "$volId" ]; then
                             echo "waiting for import to finish..."
                             while true; do
-                                volId=$(ec2-describe-conversion-tasks "$taskId" --region "$region" | sed 's/.*VolumeId.*\(vol-[0-9a-f]\+\).*/\1/ ; t ; d')
-                                if [ -n "$volId" ]; then break; fi
+                                volId=$(aws ec2 describe-conversion-tasks --conversion-task-ids "$taskId" --region "$region" | jq -r .ConversionTasks[0].ImportVolume.Volume.Id)
+                                if [ "$volId" != null ]; then break; fi
                                 sleep 10
                             done
 
@@ -143,22 +149,24 @@ for type in hvm pv; do
                         # Delete the import task.
                         if [ -n "$volId" -a -n "$taskId" ]; then
                             echo "removing import task..."
-                            ec2-delete-disk-image -t "$taskId" --region "$region" -o "$EC2_ACCESS_KEY" -w "$EC2_SECRET_KEY" || true
+                            ec2-delete-disk-image -t "$taskId" --region "$region" \
+                                -O "$AWS_ACCESS_KEY_ID" -W "$AWS_SECRET_ACCESS_KEY" \
+                                -o "$AWS_ACCESS_KEY_ID" -w "$AWS_SECRET_ACCESS_KEY" || true
                             rm -f $stateDir/$region.$type.task-id
                         fi
 
                         # Create a snapshot.
                         if [ -z "$snapId" ]; then
                             echo "creating snapshot..."
-                            snapId=$(ec2-create-snapshot "$volId" --region "$region" | cut -f 2)
+                            snapId=$(aws ec2 create-snapshot --volume-id "$volId" --region "$region" --description "$description" | jq -r .SnapshotId)
+                            if [ "$snapId" = null ]; then exit 1; fi
                             echo -n "$snapId" > $stateDir/$region.$type.snap-id
-                            ec2-create-tags "$snapId" -t "Name=$description" --region "$region"
                         fi
 
                         # Wait for the snapshot to finish.
                         echo "waiting for snapshot to finish..."
                         while true; do
-                            status=$(ec2-describe-snapshots "$snapId" --region "$region" | head -n1 | cut -f 4)
+                            status=$(aws ec2 describe-snapshots --snapshot-ids "$snapId" --region "$region" | jq -r .Snapshots[0].State)
                             if [ "$status" = completed ]; then break; fi
                             sleep 10
                         done
@@ -166,35 +174,50 @@ for type in hvm pv; do
                         # Delete the volume.
                         if [ -n "$volId" ]; then
                             echo "deleting volume..."
-                            ec2-delete-volume "$volId" --region "$region" || true
+                            aws ec2 delete-volume --volume-id "$volId" --region "$region" || true
                             rm -f $stateDir/$region.$type.vol-id
                         fi
 
-                        extraFlags="-b /dev/sda1=$snapId:$vhdFileLogicalGigaBytes:true:gp2"
+                        blockDeviceMappings="DeviceName=/dev/sda1,Ebs={SnapshotId=$snapId,VolumeSize=$vhdFileLogicalGigaBytes,DeleteOnTermination=true,VolumeType=gp2}"
+                        extraFlags=""
 
                         if [ $type = pv ]; then
-                            extraFlags+=" --root-device-name=/dev/sda1"
+                            extraFlags+=" --root-device-name /dev/sda1"
+                        else
+                            extraFlags+=" --root-device-name /dev/sda1"
+                            extraFlags+=" --sriov-net-support simple"
+                            extraFlags+=" --ena-support"
                         fi
 
-                        extraFlags+=" -b /dev/sdb=ephemeral0 -b /dev/sdc=ephemeral1 -b /dev/sdd=ephemeral2 -b /dev/sde=ephemeral3"
+                        blockDeviceMappings+=" DeviceName=/dev/sdb,VirtualName=ephemeral0"
+                        blockDeviceMappings+=" DeviceName=/dev/sdc,VirtualName=ephemeral1"
+                        blockDeviceMappings+=" DeviceName=/dev/sdd,VirtualName=ephemeral2"
+                        blockDeviceMappings+=" DeviceName=/dev/sde,VirtualName=ephemeral3"
+                    fi
+
+                    if [ $type = hvm ]; then
+                        extraFlags+=" --sriov-net-support simple"
+                        extraFlags+=" --ena-support"
                     fi
 
                     # Register the AMI.
                     if [ $type = pv ]; then
-                        kernel=$(ec2-describe-images -o amazon --filter "manifest-location=*pv-grub-hd0_1.04-$arch*" --region "$region" | cut -f 2)
-                        [ -n "$kernel" ]
+                        kernel=$(aws ec2 describe-images --owner amazon --filters "Name=name,Values=pv-grub-hd0_1.04-$arch.gz" | jq -r .Images[0].ImageId)
+                        if [ "$kernel" = null ]; then break; fi
                         echo "using PV-GRUB kernel $kernel"
                         extraFlags+=" --virtualization-type paravirtual --kernel $kernel"
                     else
                         extraFlags+=" --virtualization-type hvm"
                     fi
 
-                    ami=$(ec2-register \
-                        -n "$name" \
-                        -d "$description" \
+                    ami=$(aws ec2 register-image \
+                        --name "$name" \
+                        --description "$description" \
                         --region "$region" \
                         --architecture "$arch" \
-                        $extraFlags | cut -f 2)
+                        --block-device-mappings $blockDeviceMappings \
+                        $extraFlags | jq -r .ImageId)
+                    if [ "$ami" = null ]; then break; fi
                 fi
 
                 echo -n "$ami" > $amiFile
@@ -204,23 +227,45 @@ for type in hvm pv; do
                 ami=$(cat $amiFile)
             fi
 
-            if [ -z "$NO_WAIT" -o -z "$prevAmi" ]; then
-                echo "waiting for AMI..."
-                while true; do
-                    status=$(ec2-describe-images "$ami" --region "$region" | head -n1 | cut -f 5)
-                    if [ "$status" = available ]; then break; fi
-                    sleep 10
-                done
-
-                ec2-modify-image-attribute \
-                    --region "$region" "$ami" -l -a all
-            fi
-
             echo "region = $region, type = $type, store = $store, ami = $ami"
+
             if [ -z "$prevAmi" ]; then
                 prevAmi="$ami"
                 prevRegion="$region"
             fi
+        done
+
+    done
+
+done
+
+for type in $types; do
+    link=$stateDir/$type
+    system=x86_64-linux
+    arch=x86_64
+
+    for store in $stores; do
+
+        for region in $regions; do
+
+            name=nixos-$version-$arch-$type-$store
+            amiFile=$stateDir/$region.$type.$store.ami-id
+            ami=$(cat $amiFile)
+
+            echo "region = $region, type = $type, store = $store, ami = $ami"
+
+            echo -n "waiting for AMI..."
+            while true; do
+                status=$(aws ec2 describe-images --image-ids "$ami" --region "$region" | jq -r .Images[0].State)
+                if [ "$status" = available ]; then break; fi
+                sleep 10
+                echo -n '.'
+            done
+            echo
+
+            # Make the image public.
+            aws ec2 modify-image-attribute \
+                --image-id "$ami" --region "$region" --launch-permission 'Add={Group=all}'
 
             echo "  \"$major\".$region.$type-$store = \"$ami\";" >> ec2-amis.nix
         done
diff --git a/nixos/modules/config/fonts/fontconfig-ultimate.nix b/nixos/modules/config/fonts/fontconfig-ultimate.nix
index 02568f9de51e..47d7cc5924d4 100644
--- a/nixos/modules/config/fonts/fontconfig-ultimate.nix
+++ b/nixos/modules/config/fonts/fontconfig-ultimate.nix
@@ -3,6 +3,84 @@
 with lib;
 
 let fcBool = x: if x then "<bool>true</bool>" else "<bool>false</bool>";
+    cfg = config.fonts.fontconfig.ultimate;
+    fontconfigUltimateConf = pkgs.writeText "ultimate-conf" ''
+      <?xml version="1.0"?>
+      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+      <fontconfig>
+
+        ${optionalString (!cfg.allowBitmaps) ''
+        <!-- Reject bitmap fonts -->
+        <selectfont>
+          <rejectfont>
+            <pattern>
+              <patelt name="scalable"><bool>false</bool></patelt>
+            </pattern>
+          </rejectfont>
+        </selectfont>
+        ''}
+
+        ${optionalString cfg.allowType1 ''
+        <!-- Reject Type 1 fonts -->
+        <selectfont>
+          <rejectfont>
+            <pattern>
+              <patelt name="fontformat">
+                <string>Type 1</string>
+              </patelt>
+            </pattern>
+          </rejectfont>
+        </selectfont>
+        ''}
+
+        <!-- Use embedded bitmaps in fonts like Calibri? -->
+        <match target="font">
+          <edit name="embeddedbitmap" mode="assign">
+            ${fcBool cfg.useEmbeddedBitmaps}
+          </edit>
+        </match>
+
+        <!-- Force autohint always -->
+        <match target="font">
+          <edit name="force_autohint" mode="assign">
+            ${fcBool cfg.forceAutohint}
+          </edit>
+        </match>
+
+        <!-- Render some monospace TTF fonts as bitmaps -->
+        <match target="pattern">
+          <edit name="bitmap_monospace" mode="assign">
+            ${fcBool cfg.renderMonoTTFAsBitmap}
+          </edit>
+        </match>
+
+      </fontconfig>
+    '';
+    confPkg = 
+      let version = pkgs.fontconfig.configVersion;
+      in pkgs.runCommand "font-ultimate-conf" {} ''
+      mkdir -p $out/etc/fonts/{,${version}/}conf.d/
+
+      cp ${fontconfigUltimateConf} \
+        $out/etc/fonts/conf.d/52-fontconfig-ultimate.conf
+
+      cp ${fontconfigUltimateConf} \
+        $out/etc/fonts/${version}/conf.d/52-fontconfig-ultimate.conf
+
+      ${optionalString (cfg.substitutions != "none") ''
+      cp ${pkgs.fontconfig-ultimate.confd}/etc/fonts/presets/${cfg.substitutions}/*.conf \
+        $out/etc/fonts/conf.d/
+      cp ${pkgs.fontconfig-ultimate.confd}/etc/fonts/presets/${cfg.substitutions}/*.conf \
+        $out/etc/fonts/${version}/conf.d/
+      ''}
+
+      ln -s ${pkgs.fontconfig-ultimate.confd}/etc/fonts/conf.d/*.conf \
+        $out/etc/fonts/conf.d/
+
+      ln -s ${pkgs.fontconfig-ultimate.confd}/etc/fonts/conf.d/*.conf \
+        $out/etc/fonts/${version}/conf.d/
+    '';
+
 in
 {
 
@@ -115,78 +193,11 @@ in
   };
 
 
-  config =
-    let ultimate = config.fonts.fontconfig.ultimate;
-        fontconfigUltimateConf = ''
-          <?xml version="1.0"?>
-          <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
-          <fontconfig>
-
-            ${optionalString (!ultimate.allowBitmaps) ''
-            <!-- Reject bitmap fonts -->
-            <selectfont>
-              <rejectfont>
-                <pattern>
-                  <patelt name="scalable"><bool>false</bool></patelt>
-                </pattern>
-              </rejectfont>
-            </selectfont>
-            ''}
-
-            ${optionalString ultimate.allowType1 ''
-            <!-- Reject Type 1 fonts -->
-            <selectfont>
-              <rejectfont>
-                <pattern>
-                  <patelt name="fontformat">
-                    <string>Type 1</string>
-                  </patelt>
-                </pattern>
-              </rejectfont>
-            </selectfont>
-            ''}
-
-            <!-- Use embedded bitmaps in fonts like Calibri? -->
-            <match target="font">
-              <edit name="embeddedbitmap" mode="assign">
-                ${fcBool ultimate.useEmbeddedBitmaps}
-              </edit>
-            </match>
-
-            <!-- Force autohint always -->
-            <match target="font">
-              <edit name="force_autohint" mode="assign">
-                ${fcBool ultimate.forceAutohint}
-              </edit>
-            </match>
-
-            <!-- Render some monospace TTF fonts as bitmaps -->
-            <match target="pattern">
-              <edit name="bitmap_monospace" mode="assign">
-                ${fcBool ultimate.renderMonoTTFAsBitmap}
-              </edit>
-            </match>
-
-            ${optionalString (ultimate.substitutions != "none") ''
-            <!-- Type 1 font substitutions -->
-            <include ignore_missing="yes">${pkgs.fontconfig-ultimate.confd}/etc/fonts/presets/${ultimate.substitutions}</include>
-            ''}
-
-            <include ignore_missing="yes">${pkgs.fontconfig-ultimate.confd}/etc/fonts/conf.d</include>
-
-          </fontconfig>
-        '';
-    in mkIf (config.fonts.fontconfig.enable && ultimate.enable) {
-
-      environment.etc."fonts/conf.d/52-fontconfig-ultimate.conf" = {
-        text = fontconfigUltimateConf;
-      };
-
-      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/52-fontconfig-ultimate.conf" = {
-        text = fontconfigUltimateConf;
-      };
+  config = mkIf (config.fonts.fontconfig.enable && cfg.enable) {
 
-      environment.variables = ultimate.rendering;
+      fonts.fontconfig.confPkgs = [ confPkg ];
+      
+      environment.variables = cfg.rendering;
 
     };
 
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index 1eaebe4b2bbd..fcf5add45197 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -2,6 +2,121 @@
 
 with lib;
 
+let cfg = config.fonts.fontconfig;
+    fcBool = x: "<bool>" + (if x then "true" else "false") + "</bool>";
+    renderConf = pkgs.writeText "render-conf" ''
+      <?xml version='1.0'?>
+      <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+      <fontconfig>
+
+        <!-- Default rendering settings -->
+        <match target="font">
+          <edit mode="assign" name="hinting">
+            ${fcBool cfg.hinting.enable}
+          </edit>
+          <edit mode="assign" name="autohint">
+            ${fcBool cfg.hinting.autohint}
+          </edit>
+          <edit mode="assign" name="hintstyle">
+            <const>hint${cfg.hinting.style}</const>
+          </edit>
+          <edit mode="assign" name="antialias">
+            ${fcBool cfg.antialias}
+          </edit>
+          <edit mode="assign" name="rgba">
+            <const>${cfg.subpixel.rgba}</const>
+          </edit>
+          <edit mode="assign" name="lcdfilter">
+            <const>lcd${cfg.subpixel.lcdfilter}</const>
+          </edit>
+        </match>
+
+        ${optionalString (cfg.dpi != 0) ''
+        <match target="pattern">
+          <edit name="dpi" mode="assign">
+            <double>${toString cfg.dpi}</double>
+          </edit>
+        </match>
+        ''}
+
+      </fontconfig>
+    '';
+    genericAliasConf = 
+      let genDefault = fonts: name:
+        optionalString (fonts != []) ''
+          <alias>
+            <family>${name}</family>
+            <prefer>
+            ${concatStringsSep ""
+            (map (font: ''
+              <family>${font}</family>
+            '') fonts)}
+            </prefer>
+          </alias>
+        '';
+      in
+      pkgs.writeText "generic-alias-conf" ''
+      <?xml version='1.0'?>
+      <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+      <fontconfig>
+
+        <!-- Default fonts -->
+        ${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
+
+        ${genDefault cfg.defaultFonts.serif     "serif"}
+
+        ${genDefault cfg.defaultFonts.monospace "monospace"}
+
+      </fontconfig>
+    '';
+    cacheConf = let
+      cache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
+    in
+    pkgs.writeText "cache-conf" ''
+      <?xml version='1.0'?>
+      <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+      <fontconfig>
+        <!-- Font directories -->
+        ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
+        <!-- Pre-generated font caches -->
+        <cachedir>${cache pkgs.fontconfig}</cachedir>
+        ${optionalString (pkgs.stdenv.isx86_64 && cfg.cache32Bit) ''
+          <cachedir>${cache pkgs.pkgsi686Linux.fontconfig}</cachedir>
+        ''}
+      </fontconfig>
+    '';
+    userConf = pkgs.writeText "user-conf" ''
+      <?xml version="1.0"?>
+      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+      <fontconfig>
+        <include ignore_missing="yes" prefix="xdg">fontconfig/conf.d</include>
+        <include ignore_missing="yes" prefix="xdg">fontconfig/fonts.conf</include>
+      </fontconfig>
+    '';
+    fontsConf = pkgs.makeFontsConf { fontconfig = pkgs.fontconfig_210; fontDirectories = config.fonts.fonts; };
+    confPkg = 
+      let version = pkgs.fontconfig.configVersion;
+      in pkgs.runCommand "fontconfig-conf" {} ''
+      mkdir -p $out/etc/fonts/{,${version}/}conf.d
+
+      ln -s ${fontsConf}        $out/etc/fonts/fonts.conf
+
+      ln -s ${pkgs.fontconfig.out}/etc/fonts/fonts.conf $out/etc/fonts/${version}/fonts.conf
+      ln -s ${pkgs.fontconfig.out}/etc/fonts/conf.d/*   $out/etc/fonts/${version}/conf.d/
+
+      ln -s ${renderConf}       $out/etc/fonts/conf.d/10-nixos-rendering.conf
+      ln -s ${genericAliasConf} $out/etc/fonts/conf.d/60-nixos-generic-alias.conf
+
+      ln -s ${cacheConf}        $out/etc/fonts/${version}/conf.d/00-nixos.conf
+
+      ln -s ${renderConf}       $out/etc/fonts/${version}/conf.d/10-nixos-rendering.conf
+      ln -s ${genericAliasConf} $out/etc/fonts/${version}/conf.d/30-nixos-generic-alias.conf
+
+      ${optionalString cfg.includeUserConf
+      "ln -s ${userConf}        $out/etc/fonts/${version}/conf.d/99-user.conf"}
+      
+    '';
+in
 {
 
   options = {
@@ -21,6 +136,15 @@ with lib;
           '';
         };
 
+        confPkgs = mkOption {
+          internal = true;
+          type     = with types; listOf path;
+          default  = [ ];
+          description = ''
+            Fontconfig configuration packages.
+          '';
+        };
+
         antialias = mkOption {
           type = types.bool;
           default = true;
@@ -143,135 +267,17 @@ with lib;
 
   };
 
-  config =
-    let fontconfig = config.fonts.fontconfig;
-        fcBool = x: "<bool>" + (if x then "true" else "false") + "</bool>";
-        renderConf = ''
-          <?xml version='1.0'?>
-          <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
-          <fontconfig>
-
-            <!-- Default rendering settings -->
-            <match target="font">
-              <edit mode="assign" name="hinting">
-                ${fcBool fontconfig.hinting.enable}
-              </edit>
-              <edit mode="assign" name="autohint">
-                ${fcBool fontconfig.hinting.autohint}
-              </edit>
-              <edit mode="assign" name="hintstyle">
-                <const>hint${fontconfig.hinting.style}</const>
-              </edit>
-              <edit mode="assign" name="antialias">
-                ${fcBool fontconfig.antialias}
-              </edit>
-              <edit mode="assign" name="rgba">
-                <const>${fontconfig.subpixel.rgba}</const>
-              </edit>
-              <edit mode="assign" name="lcdfilter">
-                <const>lcd${fontconfig.subpixel.lcdfilter}</const>
-              </edit>
-            </match>
-
-            ${optionalString (fontconfig.dpi != 0) ''
-            <match target="pattern">
-              <edit name="dpi" mode="assign">
-                <double>${toString fontconfig.dpi}</double>
-              </edit>
-            </match>
-            ''}
-
-          </fontconfig>
-        '';
-        genericAliasConf = ''
-          <?xml version='1.0'?>
-          <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
-          <fontconfig>
-
-            <!-- Default fonts -->
-            ${optionalString (fontconfig.defaultFonts.sansSerif != []) ''
-            <alias>
-              <family>sans-serif</family>
-              <prefer>
-                ${concatStringsSep "\n"
-                  (map (font: "<family>${font}</family>")
-                    fontconfig.defaultFonts.sansSerif)}
-              </prefer>
-            </alias>
-            ''}
-            ${optionalString (fontconfig.defaultFonts.serif != []) ''
-            <alias>
-              <family>serif</family>
-              <prefer>
-                ${concatStringsSep "\n"
-                  (map (font: "<family>${font}</family>")
-                    fontconfig.defaultFonts.serif)}
-              </prefer>
-            </alias>
-            ''}
-            ${optionalString (fontconfig.defaultFonts.monospace != []) ''
-            <alias>
-              <family>monospace</family>
-              <prefer>
-                ${concatStringsSep "\n"
-                  (map (font: "<family>${font}</family>")
-                    fontconfig.defaultFonts.monospace)}
-              </prefer>
-            </alias>
-            ''}
-
-          </fontconfig>
-        '';
-    in mkIf fontconfig.enable {
-
-      # Fontconfig 2.10 backward compatibility
-
-      # Bring in the default (upstream) fontconfig configuration, only for fontconfig 2.10
-      environment.etc."fonts/fonts.conf".source =
-        pkgs.makeFontsConf { fontconfig = pkgs.fontconfig_210; fontDirectories = config.fonts.fonts; };
-
-      environment.etc."fonts/conf.d/10-nixos-rendering.conf".text = renderConf;
-      environment.etc."fonts/conf.d/60-nixos-generic-alias.conf".text = genericAliasConf;
-
-      # Versioned fontconfig > 2.10. Take shared fonts.conf from fontconfig.
-      # Otherwise specify only font directories.
-      environment.etc."fonts/${pkgs.fontconfig.configVersion}/fonts.conf".source =
-        "${pkgs.fontconfig.out}/etc/fonts/fonts.conf";
-
-      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/00-nixos.conf".text =
-        let
-          cache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
-        in ''
-          <?xml version='1.0'?>
-          <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
-          <fontconfig>
-            <!-- Font directories -->
-            ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
-            <!-- Pre-generated font caches -->
-            <cachedir>${cache pkgs.fontconfig}</cachedir>
-            ${optionalString (pkgs.stdenv.isx86_64 && config.fonts.fontconfig.cache32Bit) ''
-              <cachedir>${cache pkgs.pkgsi686Linux.fontconfig}</cachedir>
-            ''}
-          </fontconfig>
-        '';
-
-      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/10-nixos-rendering.conf".text = renderConf;
-      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/60-nixos-generic-alias.conf".text = genericAliasConf;
-
-      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/99-user.conf" = {
-        enable = fontconfig.includeUserConf;
-        text = ''
-          <?xml version="1.0"?>
-          <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
-          <fontconfig>
-            <include ignore_missing="yes" prefix="xdg">fontconfig/conf.d</include>
-            <include ignore_missing="yes" prefix="xdg">fontconfig/fonts.conf</include>
-          </fontconfig>
-        '';
-      };
+  config = mkIf cfg.enable {
+    fonts.fontconfig.confPkgs = [ confPkg ];
 
-      environment.systemPackages = [ pkgs.fontconfig ];
+    environment.etc.fonts.source = 
+      let fontConf = pkgs.symlinkJoin {
+            name  = "fontconfig-etc";
+            paths = cfg.confPkgs;
+          };
+      in "${fontConf}/etc/fonts/";
 
-    };
+    environment.systemPackages = [ pkgs.fontconfig ];
+  };
 
 }
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index b20fac6ad3e2..e341931aacce 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -41,6 +41,15 @@ in
         '';
       };
 
+      consolePackages = mkOption {
+        type = types.listOf types.package;
+        default = with pkgs.kbdKeymaps; [ dvp neo ];
+        description = ''
+	  List of additional packages that provide console fonts, keymaps and
+          other resources.
+        '';
+      };
+
       consoleFont = mkOption {
         type = types.str;
         default = "Lat2-Terminus16";
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 9642981803bf..f458bc39adaa 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -1,7 +1,7 @@
 # This module defines a global environment configuration and
 # a common configuration for all shells.
 
-{ config, lib, pkgs, ... }:
+{ config, lib, utils, pkgs, ... }:
 
 with lib;
 
@@ -135,13 +135,13 @@ in
 
     environment.shells = mkOption {
       default = [];
-      example = [ "/run/current-system/sw/bin/zsh" ];
+      example = literalExample "[ pkgs.bashInteractive pkgs.zsh ]";
       description = ''
         A list of permissible login shells for user accounts.
         No need to mention <literal>/bin/sh</literal>
         here, it is placed into this list implicitly.
       '';
-      type = types.listOf types.path;
+      type = types.listOf (types.either types.shellPackage types.path);
     };
 
   };
@@ -158,7 +158,7 @@ in
 
     environment.etc."shells".text =
       ''
-        ${concatStringsSep "\n" cfg.shells}
+        ${concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
         /bin/sh
       '';
 
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index f0353c5a35ec..62b6e011713e 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -30,8 +30,7 @@ let
         description = ''
           If this option is set, ‘device’ is interpreted as the
           path of a swapfile that will be created automatically
-          with the indicated size (in megabytes) if it doesn't
-          exist.
+          with the indicated size (in megabytes).
         '';
       };
 
@@ -132,9 +131,13 @@ in
             script =
               ''
                 ${optionalString (sw.size != null) ''
-                  if [ ! -e "${sw.device}" ]; then
+                  currentSize=$(( $(stat -c "%s" "${sw.device}" 2>/dev/null || echo 0) / 1024 / 1024 ))
+                  if [ "${toString sw.size}" != "$currentSize" ]; then
                     fallocate -l ${toString sw.size}M "${sw.device}" ||
                       dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size}
+                    if [ "${toString sw.size}" -lt "$currentSize" ]; then
+                      truncate --size "${toString sw.size}M" "${sw.device}"
+                    fi
                     chmod 0600 ${sw.device}
                     ${optionalString (!sw.randomEncryption) "mkswap ${sw.realDevice}"}
                   fi
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index de73de91629b..967f427374b1 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -103,7 +103,7 @@ foreach my $g (@{$spec->{groups}}) {
     if (defined $existing) {
         $g->{gid} = $existing->{gid} if !defined $g->{gid};
         if ($g->{gid} != $existing->{gid}) {
-            warn "warning: not applying GID change of group ‘$name’\n";
+            warn "warning: not applying GID change of group ‘$name’ ($existing->{gid} -> $g->{gid})\n";
             $g->{gid} = $existing->{gid};
         }
         $g->{password} = $existing->{password}; # do we want this?
@@ -163,7 +163,7 @@ foreach my $u (@{$spec->{users}}) {
     if (defined $existing) {
         $u->{uid} = $existing->{uid} if !defined $u->{uid};
         if ($u->{uid} != $existing->{uid}) {
-            warn "warning: not applying UID change of user ‘$name’\n";
+            warn "warning: not applying UID change of user ‘$name’ ($existing->{uid} -> $u->{uid})\n";
             $u->{uid} = $existing->{uid};
         }
     } else {
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index e643b2d059b5..d92deb85d2a5 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -1,9 +1,8 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, utils, pkgs, ... }:
 
 with lib;
 
 let
-
   ids = config.ids;
   cfg = config.users;
 
@@ -103,7 +102,7 @@ let
       };
 
       home = mkOption {
-        type = types.str;
+        type = types.path;
         default = "/var/empty";
         description = "The user's home directory.";
       };
@@ -118,9 +117,17 @@ let
       };
 
       shell = mkOption {
-        type = types.str;
-        default = "/run/current-system/sw/bin/nologin";
-        description = "The path to the user's shell.";
+        type = types.either types.shellPackage types.path;
+        default = pkgs.nologin;
+        defaultText = "pkgs.nologin";
+        example = literalExample "pkgs.bashInteractive";
+        description = ''
+          The path to the user's shell. Can use shell derivations,
+          like <literal>pkgs.bashInteractive</literal>. Don’t
+          forget to enable your shell in
+          <literal>programs</literal> if necessary,
+          like <code>programs.zsh.enable = true;</code>.
+        '';
       };
 
       subUidRanges = mkOption {
@@ -359,11 +366,12 @@ let
 
   spec = pkgs.writeText "users-groups.json" (builtins.toJSON {
     inherit (cfg) mutableUsers;
-    users = mapAttrsToList (n: u:
+    users = mapAttrsToList (_: u:
       { inherit (u)
-          name uid group description home shell createHome isSystemUser
+          name uid group description home createHome isSystemUser
           password passwordFile hashedPassword
           initialPassword initialHashedPassword;
+        shell = utils.toShellPath u.shell;
       }) cfg.users;
     groups = mapAttrsToList (n: g:
       { inherit (g) name gid;
@@ -373,6 +381,12 @@ let
       }) cfg.groups;
   });
 
+  systemShells =
+    let
+      shells = mapAttrsToList (_: u: u.shell) cfg.users;
+    in
+      filter types.shellPackage.check shells;
+
 in {
 
   ###### interface
@@ -468,7 +482,6 @@ in {
         home = "/root";
         shell = mkDefault cfg.defaultUserShell;
         group = "root";
-        extraGroups = [ "grsecurity" ];
         initialHashedPassword = mkDefault config.security.initialRootPassword;
       };
       nobody = {
@@ -478,6 +491,9 @@ in {
       };
     };
 
+    # Install all the user shells
+    environment.systemPackages = systemShells;
+
     users.groups = {
       root.gid = ids.gids.root;
       wheel.gid = ids.gids.wheel;
@@ -497,7 +513,6 @@ in {
       nixbld.gid = ids.gids.nixbld;
       utmp.gid = ids.gids.utmp;
       adm.gid = ids.gids.adm;
-      grsecurity.gid = ids.gids.grsecurity;
       input.gid = ids.gids.input;
     };
 
diff --git a/nixos/modules/i18n/input-method/fcitx.nix b/nixos/modules/i18n/input-method/fcitx.nix
index bdefd2d2d596..e97bb9f80eb5 100644
--- a/nixos/modules/i18n/input-method/fcitx.nix
+++ b/nixos/modules/i18n/input-method/fcitx.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.i18n.inputMethod.fcitx;
-  fcitxPackage = pkgs.fcitx-with-plugins.override { plugins = cfg.engines; };
+  fcitxPackage = pkgs.fcitx.override { plugins = cfg.engines; };
   fcitxEngine = types.package // {
     name  = "fcitx-engine";
     check = x: (lib.types.package.check x) && (attrByPath ["meta" "isFcitxEngine"] false x);
diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
index 957a8ff9ce6d..80fb47072286 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -19,18 +19,37 @@ in
       "it cannot be cross compiled";
   };
 
+  # Needed by RPi firmware
+  nixpkgs.config.allowUnfree = true;
+
   boot.loader.grub.enable = false;
   boot.loader.generic-extlinux-compatible.enable = true;
 
   boot.kernelPackages = pkgs.linuxPackages_latest;
-  boot.kernelParams = ["console=ttyS0,115200n8" "console=ttymxc0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"];
+  boot.kernelParams = ["console=ttyS0,115200n8" "console=ttymxc0,115200n8" "console=ttyAMA0,115200n8" "console=ttyO0,115200n8" "console=tty0"];
+  boot.consoleLogLevel = 7;
 
   # FIXME: this probably should be in installation-device.nix
   users.extraUsers.root.initialHashedPassword = "";
 
   sdImage = {
-    populateBootCommands = ''
+    populateBootCommands = let
+      configTxt = pkgs.writeText "config.txt" ''
+        [pi2]
+        kernel=u-boot-rpi2.bin
+
+        [pi3]
+        kernel=u-boot-rpi3.bin
+        enable_uart=1
+      '';
+      in ''
+        for f in bootcode.bin fixup.dat start.elf; do
+          cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/$f boot/
+        done
+        cp ${pkgs.ubootRaspberryPi2}/u-boot.bin boot/u-boot-rpi2.bin
+        cp ${pkgs.ubootRaspberryPi3}/u-boot.bin boot/u-boot-rpi3.bin
+        cp ${configTxt} boot/config.txt
         ${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./boot
-    '';
+      '';
   };
 }
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index e7163f10a3c3..dc196468139a 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -26,6 +26,7 @@ in
   boot.loader.generic-extlinux-compatible.enable = true;
 
   boot.kernelPackages = pkgs.linuxPackages_rpi;
+  boot.consoleLogLevel = 7;
 
   # FIXME: this probably should be in installation-device.nix
   users.extraUsers.root.initialHashedPassword = "";
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index c23d7e5b509d..7962be137875 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -91,12 +91,10 @@ ln -s /run $mountPoint/var/run
 rm -f $mountPoint/etc/{resolv.conf,hosts}
 cp -Lf /etc/resolv.conf /etc/hosts $mountPoint/etc/
 
-if [ -e "$SSL_CERT_FILE" ]; then
-    cp -Lf "$SSL_CERT_FILE" "$mountPoint/tmp/ca-cert.crt"
-    export SSL_CERT_FILE=/tmp/ca-cert.crt
-    # For Nix 1.7
-    export CURL_CA_BUNDLE=/tmp/ca-cert.crt
-fi
+cp -Lf "@cacert@" "$mountPoint/tmp/ca-cert.crt"
+export SSL_CERT_FILE=/tmp/ca-cert.crt
+# For Nix 1.7
+export CURL_CA_BUNDLE=/tmp/ca-cert.crt
 
 if [ -n "$runChroot" ]; then
     if ! [ -L $mountPoint/nix/var/nix/profiles/system ]; then
diff --git a/nixos/modules/installer/tools/nixos-version.sh b/nixos/modules/installer/tools/nixos-version.sh
index 51aa2dd82321..77a1b458a342 100644
--- a/nixos/modules/installer/tools/nixos-version.sh
+++ b/nixos/modules/installer/tools/nixos-version.sh
@@ -1,6 +1,10 @@
 #! @shell@
 
 case "$1" in
+  -h|--help)
+    exec man nixos-version
+    exit 1
+    ;;
   --hash|--revision)
     echo "@nixosRevision@"
     ;;
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index b8fd9deaf1e4..d8622b510522 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -23,6 +23,7 @@ let
 
     inherit (pkgs) perl pathsFromGraph;
     nix = config.nix.package.out;
+    cacert = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
 
     nixClosure = pkgs.runCommand "closure"
       { exportReferencesGraph = ["refs" config.nix.package.out]; }
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index d421167c859c..581cd4fb631f 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -147,7 +147,6 @@
       foundationdb = 118;
       newrelic = 119;
       starbound = 120;
-      #grsecurity = 121; # unused
       hydra = 122;
       spiped = 123;
       teamspeak = 124;
@@ -269,6 +268,8 @@
       nzbget = 245;
       mosquitto = 246;
       toxvpn = 247;
+      squeezelite = 248;
+      turnserver = 249;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -369,7 +370,7 @@
       quassel = 89;
       amule = 90;
       minidlna = 91;
-      #elasticsearch = 92; # unused
+      elasticsearch = 92;
       #tcpcryptd = 93; # unused
       connman = 94;
       firebird = 95;
@@ -396,7 +397,6 @@
       foundationdb = 118;
       newrelic = 119;
       starbound = 120;
-      grsecurity = 121;
       hydra = 122;
       spiped = 123;
       teamspeak = 124;
@@ -508,6 +508,8 @@
       nzbget = 245;
       mosquitto = 246;
       #toxvpn = 247; # unused
+      #squeezelite = 248; #unused
+      turnserver = 249;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 37a200c09588..0b3f49aec5c9 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -76,6 +76,7 @@
   ./programs/screen.nix
   ./programs/shadow.nix
   ./programs/shell.nix
+  ./programs/spacefm.nix
   ./programs/ssh.nix
   ./programs/ssmtp.nix
   ./programs/tmux.nix
@@ -110,6 +111,7 @@
   ./services/audio/liquidsoap.nix
   ./services/audio/mpd.nix
   ./services/audio/mopidy.nix
+  ./services/audio/squeezelite.nix
   ./services/backup/almir.nix
   ./services/backup/bacula.nix
   ./services/backup/crashplan.nix
@@ -125,10 +127,11 @@
   ./services/computing/torque/server.nix
   ./services/computing/torque/mom.nix
   ./services/computing/slurm/slurm.nix
+  ./services/continuous-integration/buildkite-agent.nix
+  ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/jenkins/default.nix
-  ./services/continuous-integration/jenkins/slave.nix
   ./services/continuous-integration/jenkins/job-builder.nix
-  ./services/continuous-integration/hydra/default.nix
+  ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
   ./services/databases/couchdb.nix
@@ -162,6 +165,7 @@
   ./services/desktops/profile-sync-daemon.nix
   ./services/desktops/telepathy.nix
   ./services/development/hoogle.nix
+  ./services/editors/emacs.nix
   ./services/games/factorio.nix
   ./services/games/ghost-one.nix
   ./services/games/minecraft-server.nix
@@ -218,6 +222,7 @@
   ./services/misc/confd.nix
   ./services/misc/devmon.nix
   ./services/misc/dictd.nix
+  ./services/misc/dysnomia.nix
   ./services/misc/disnix.nix
   ./services/misc/docker-registry.nix
   ./services/misc/emby.nix
@@ -314,6 +319,7 @@
   ./services/networking/cntlm.nix
   ./services/networking/connman.nix
   ./services/networking/consul.nix
+  ./services/networking/coturn.nix
   ./services/networking/ddclient.nix
   ./services/networking/dhcpcd.nix
   ./services/networking/dhcpd.nix
@@ -362,6 +368,7 @@
   ./services/networking/ntopng.nix
   ./services/networking/ntpd.nix
   ./services/networking/nylon.nix
+  ./services/networking/offlineimap.nix
   ./services/networking/oidentd.nix
   ./services/networking/openfire.nix
   ./services/networking/openntpd.nix
@@ -369,6 +376,7 @@
   ./services/networking/ostinato.nix
   ./services/networking/pdnsd.nix
   ./services/networking/polipo.nix
+  ./services/networking/pptpd.nix
   ./services/networking/prayer.nix
   ./services/networking/privoxy.nix
   ./services/networking/prosody.nix
@@ -409,6 +417,7 @@
   ./services/networking/wicd.nix
   ./services/networking/wpa_supplicant.nix
   ./services/networking/xinetd.nix
+  ./services/networking/xl2tpd.nix
   ./services/networking/zerobin.nix
   ./services/networking/zerotierone.nix
   ./services/networking/znc.nix
@@ -455,6 +464,7 @@
   ./services/web-servers/lighttpd/cgit.nix
   ./services/web-servers/lighttpd/default.nix
   ./services/web-servers/lighttpd/gitweb.nix
+  ./services/web-servers/lighttpd/inginious.nix
   ./services/web-servers/nginx/default.nix
   ./services/web-servers/phpfpm.nix
   ./services/web-servers/shellinabox.nix
@@ -490,6 +500,7 @@
   ./services/x11/window-managers/windowlab.nix
   ./services/x11/window-managers/wmii.nix
   ./services/x11/window-managers/xmonad.nix
+  ./services/x11/xbanish.nix
   ./services/x11/xfs.nix
   ./services/x11/xserver.nix
   ./system/activation/activation-script.nix
@@ -513,6 +524,7 @@
   ./system/boot/luksroot.nix
   ./system/boot/modprobe.nix
   ./system/boot/networkd.nix
+  ./system/boot/plymouth.nix
   ./system/boot/resolved.nix
   ./system/boot/shutdown.nix
   ./system/boot/stage-1.nix
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index 669b6975c690..93736ba256b3 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -42,7 +42,7 @@ with lib;
 
         The "root" account has an empty password.  ${
           optionalString config.services.xserver.enable
-            "Type `start display-manager' to\nstart the graphical user interface."}
+            "Type `systemctl start display-manager' to\nstart the graphical user interface."}
       '';
 
     # Allow sshd to be started manually through "start sshd".
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index e4e264ec0036..c09bcfb70e24 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -200,7 +200,7 @@ in
     # Configuration for readline in bash.
     environment.etc."inputrc".source = ./inputrc;
 
-    users.defaultUserShell = mkDefault "/run/current-system/sw/bin/bash";
+    users.defaultUserShell = mkDefault pkgs.bashInteractive;
 
     environment.pathsToLink = optionals cfg.enableCompletion [
       "/etc/bash_completion.d"
diff --git a/nixos/modules/programs/bash/inputrc b/nixos/modules/programs/bash/inputrc
index e4eabc052c5f..f339eb649ed8 100644
--- a/nixos/modules/programs/bash/inputrc
+++ b/nixos/modules/programs/bash/inputrc
@@ -6,6 +6,7 @@ set meta-flag on
 set input-meta on
 set convert-meta off
 set output-meta on
+set colored-stats on
 
 #set mark-symlinked-directories on
 
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 566398d839fd..6398509357a6 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -1,6 +1,6 @@
 # Configuration for the pwdutils suite of tools: passwd, useradd, etc.
 
-{ config, lib, pkgs, ... }:
+{ config, lib, utils, pkgs, ... }:
 
 with lib;
 
@@ -43,13 +43,13 @@ in
     users.defaultUserShell = lib.mkOption {
       description = ''
         This option defines the default shell assigned to user
-        accounts.  This must not be a store path, since the path is
+        accounts. This can be either a full system path or a shell package.
+
+        This must not be a store path, since the path is
         used outside the store (in particular in /etc/passwd).
-        Rather, it should be the path of a symlink that points to the
-        actual shell in the Nix store.
       '';
-      example = "/run/current-system/sw/bin/zsh";
-      type = types.path;
+      example = literalExample "pkgs.zsh";
+      type = types.either types.path types.shellPackage;
     };
 
   };
@@ -60,7 +60,9 @@ in
   config = {
 
     environment.systemPackages =
-      lib.optional config.users.mutableUsers pkgs.shadow;
+      lib.optional config.users.mutableUsers pkgs.shadow ++
+      lib.optional (types.shellPackage.check config.users.defaultUserShell)
+        config.users.defaultUserShell;
 
     environment.etc =
       [ { # /etc/login.defs: global configuration for pwdutils.  You
@@ -74,7 +76,7 @@ in
             ''
               GROUP=100
               HOME=/home
-              SHELL=${config.users.defaultUserShell}
+              SHELL=${utils.toShellPath config.users.defaultUserShell}
             '';
           target = "default/useradd";
         }
diff --git a/nixos/modules/programs/spacefm.nix b/nixos/modules/programs/spacefm.nix
new file mode 100644
index 000000000000..6d03608402fc
--- /dev/null
+++ b/nixos/modules/programs/spacefm.nix
@@ -0,0 +1,55 @@
+# Global configuration for spacefm.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.spacefm;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    programs.spacefm = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to install SpaceFM and create <filename>/etc/spacefm/spacefm.conf</filename>.
+        '';
+      };
+
+      settings = mkOption {
+        type = types.attrs;
+        default = {
+          tmp_dir = "/tmp";
+          terminal_su = "${pkgs.sudo}/bin/sudo";
+          graphical_su = "${pkgs.gksu}/bin/gksu";
+        };
+        example = literalExample ''{
+          tmp_dir = "/tmp";
+          terminal_su = "''${pkgs.sudo}/bin/sudo";
+          graphical_su = "''${pkgs.gksu}/bin/gksu";
+        }'';
+        description = ''
+          The system-wide spacefm configuration.
+          Parameters to be written to <filename>/etc/spacefm/spacefm.conf</filename>.
+          Refer to the <link xlink:href="https://ignorantguru.github.io/spacefm/spacefm-manual-en.html#programfiles-etc">relevant entry</link> in the SpaceFM manual.
+        '';
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.spaceFM ];
+
+    environment.etc."spacefm/spacefm.conf".text =
+      concatStrings (mapAttrsToList (n: v: "${n}=${toString v}\n") cfg.settings);
+  };
+}
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 3440261c3965..634f91a275d3 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -114,6 +114,26 @@ with lib;
     (mkRenamedOptionModule [ "services" "iodined" "extraConfig" ] [ "services" "iodine" "server" "extraConfig" ])
     (mkRemovedOptionModule [ "services" "iodined" "client" ])
 
+    # Grsecurity
+    (mkRemovedOptionModule [ "security" "grsecurity" "kernelPatch" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "mode" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "priority" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "system" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationConfig" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "hardwareVirtualisation" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationSoftware" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "sysctl" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootChmod" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootCaps" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyUSB" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProc" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProcWithGroup" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "unrestrictProcGid" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableRBAC" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableSimultConnect" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "verboseVersion" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "kernelExtraConfig" ])
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ])
     (mkRemovedOptionModule [ "programs" "bash" "enable" ])
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index ef6da788e619..f646602221a4 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -187,7 +187,7 @@ in
                   script = ''
                     cd '${cpath}'
                     set +e
-                    simp_le ${concatMapStringsSep " " (arg: escapeShellArg (toString arg)) cmdline}
+                    simp_le ${escapeShellArgs cmdline}
                     EXITCODE=$?
                     set -e
                     echo "$EXITCODE" > /tmp/lastExitCode
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
index 3f24118ea1cb..9d0249820d5d 100644
--- a/nixos/modules/security/grsecurity.nix
+++ b/nixos/modules/security/grsecurity.nix
@@ -1,312 +1,122 @@
-{ config, lib, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 with lib;
 
 let
   cfg = config.security.grsecurity;
-
-  customGrsecPkg =
-    (import ../../../pkgs/build-support/grsecurity {
-      grsecOptions = cfg;
-      inherit pkgs lib;
-    }).grsecPackage;
+  grsecLockPath = "/proc/sys/kernel/grsecurity/grsec_lock";
+
+  # Ascertain whether ZFS is required for booting the system; grsecurity is
+  # currently incompatible with ZFS, rendering the system unbootable.
+  zfsNeededForBoot = filter
+    (fs: (fs.neededForBoot
+          || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ])
+          && fs.fsType == "zfs")
+    (attrValues config.fileSystems) != [];
 in
-{
-  options = {
-    security.grsecurity = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable grsecurity support. This enables advanced exploit
-          hardening for the Linux kernel, and adds support for
-          administrative Role-Based Acess Control (RBAC) via
-          <literal>gradm</literal>. It also includes traditional
-          utilities for PaX.
-        '';
-      };
-
-      kernelPatch = mkOption {
-        type = types.attrs;
-        example = lib.literalExample "pkgs.kernelPatches.grsecurity_4_1";
-        description = ''
-          Grsecurity patch to use.
-        '';
-      };
-
-      config = {
-        mode = mkOption {
-          type = types.enum [ "auto" "custom" ];
-          default = "auto";
-          description = ''
-            grsecurity configuration mode. This specifies whether
-            grsecurity is auto-configured or otherwise completely
-            manually configured.
-          '';
-        };
-
-        priority = mkOption {
-          type = types.enum [ "security" "performance" ];
-          default = "security";
-          description = ''
-            grsecurity configuration priority. This specifies whether
-            the kernel configuration should emphasize speed or
-            security.
-          '';
-        };
-
-        system = mkOption {
-          type = types.enum [ "desktop" "server" ];
-          default = "desktop";
-          description = ''
-            grsecurity system configuration.
-          '';
-        };
 
-        virtualisationConfig = mkOption {
-          type = types.nullOr (types.enum [ "host" "guest" ]);
-          default = null;
-          description = ''
-            grsecurity virtualisation configuration. This specifies
-            the virtualisation role of the machine - that is, whether
-            it will be a virtual machine guest, a virtual machine
-            host, or neither.
-          '';
-        };
-
-        hardwareVirtualisation = mkOption {
-          type = types.nullOr types.bool;
-          default = null;
-          example = true;
-          description = ''
-            grsecurity hardware virtualisation configuration. Set to
-            <literal>true</literal> if your machine supports hardware
-            accelerated virtualisation.
-          '';
-        };
-
-        virtualisationSoftware = mkOption {
-          type = types.nullOr (types.enum [ "kvm" "xen" "vmware" "virtualbox" ]);
-          default = null;
-          description = ''
-            Configure grsecurity for use with this virtualisation software.
-          '';
-        };
-
-        sysctl = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_SYSCTL y</literal>. If
-            enabled then grsecurity can be controlled using sysctl
-            (and turned off). You are advised to *never* enable this,
-            but if you do, make sure to always set the sysctl
-            <literal>kernel.grsecurity.grsec_lock</literal> to
-            non-zero as soon as all sysctl options are set. *THIS IS
-            EXTREMELY IMPORTANT*!
-          '';
-        };
-
-        denyChrootChmod = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_CHROOT_CHMOD
-            y</literal>. If enabled, this denies processes inside a
-            chroot from setting the suid or sgid bits using
-            <literal>chmod</literal> or <literal>fchmod</literal>.
-
-            By default this protection is disabled - it makes it
-            impossible to use Nix to build software on your system,
-            which is what most users want.
+{
+  options.security.grsecurity = {
+
+    enable = mkEnableOption "Grsecurity/PaX";
+
+    lockTunables = mkOption {
+      type = types.bool;
+      example = false;
+      default = true;
+      description = ''
+        Whether to automatically lock grsecurity tunables
+        (<option>boot.kernel.sysctl."kernel.grsecurity.*"</option>).  Disable
+        this to allow configuration of grsecurity features while the system is
+        running.  The lock can be manually engaged by activating the
+        <literal>grsec-lock</literal> service unit.
+      '';
+    };
 
-            If you are using NixOps to deploy your software to a
-            remote machine, you're encouraged to enable this as you
-            won't need to compile code.
-          '';
-        };
+  };
 
-        denyChrootCaps = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Whether to lower capabilities of all processes within a chroot,
-            preventing commands that require <literal>CAP_SYS_ADMIN</literal>.
+  config = mkIf cfg.enable {
 
-            This protection is disabled by default because it breaks
-            <literal>nixos-rebuild</literal>. Whenever possible, it is
-            highly recommended to enable this protection.
-          '';
-        };
+    # Allow the user to select a different package set, subject to the stated
+    # required kernel config
+    boot.kernelPackages = mkDefault pkgs.linuxPackages_grsec_nixos;
 
-        denyUSB = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERNSEC_DENYUSB y</literal>.
+    system.requiredKernelConfig = with config.lib.kernelConfig;
+      [ (isEnabled "GRKERNSEC")
+        (isEnabled "PAX")
+        (isYES "GRKERNSEC_SYSCTL")
+        (isYES "GRKERNSEC_SYSCTL_DISTRO")
+      ];
 
-            This enables a sysctl with name
-            <literal>kernel.grsecurity.deny_new_usb</literal>. Setting
-            its value to <literal>1</literal> will prevent any new USB
-            devices from being recognized by the OS.  Any attempted
-            USB device insertion will be logged.
+    # Crashing on an overflow in kernel land is user unfriendly and may prevent
+    # the system from booting, which is too severe for our use case.
+    boot.kernelParams = [ "pax_size_overflow_report_only" ];
 
-            This option is intended to be used against custom USB
-            devices designed to exploit vulnerabilities in various USB
-            device drivers.
-          '';
-        };
+    # Install PaX related utillities into the system profile.  Eventually, we
+    # also want to include gradm here.
+    environment.systemPackages = with pkgs; [ paxctl pax-utils ];
 
-        restrictProc = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_PROC_USER
-            y</literal>. This restricts non-root users to only viewing
-            their own processes and restricts network-related
-            information, kernel symbols, and module information.
-          '';
-        };
+    # Install rules for the grsec device node
+    services.udev.packages = [ pkgs.gradm ];
 
-        restrictProcWithGroup = mkOption {
-          type = types.bool;
-          default = true;
-          description = ''
-            If true, then set <literal>GRKERN_PROC_USERGROUP
-            y</literal>. This is similar to
-            <literal>restrictProc</literal> except it allows a special
-            group (specified by <literal>unrestrictProcGid</literal>)
-            to still access otherwise classified information in
-            <literal>/proc</literal>.
-          '';
-        };
+    # This service unit is responsible for locking the Grsecurity tunables.  The
+    # unit is always defined, but only activated on bootup if lockTunables is
+    # toggled.  When lockTunables is toggled, failure to activate the unit will
+    # enter emergency mode.  The intent is to make it difficult to silently
+    # enter multi-user mode without having locked the tunables.  Some effort is
+    # made to ensure that starting the unit is an idempotent operation.
+    systemd.services.grsec-lock = {
+      description = "Lock grsecurity tunables";
 
-        unrestrictProcGid = mkOption {
-          type = types.int;
-          default = config.ids.gids.grsecurity;
-          description = ''
-            If set, specifies a GID which is exempt from
-            <literal>/proc</literal> restrictions (set by
-            <literal>GRKERN_PROC_USERGROUP</literal>). By default,
-            this is set to the GID for <literal>grsecurity</literal>,
-            a predefined NixOS group, which the
-            <literal>root</literal> account is a member of. You may
-            conveniently add other users to this group if you need
-            access to <literal>/proc</literal>
-          '';
-        };
+      wantedBy = optional cfg.lockTunables "multi-user.target";
 
-        disableRBAC = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_NO_RBAC
-            y</literal>. This disables the
-            <literal>/dev/grsec</literal> device, which in turn
-            disables the RBAC system (and <literal>gradm</literal>).
-          '';
-        };
+      wants = [ "local-fs.target" "systemd-sysctl.service" ];
+      after = [ "local-fs.target" "systemd-sysctl.service" ];
+      conflicts = [ "shutdown.target" ];
 
-        disableSimultConnect = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Disable TCP simultaneous connect.  The TCP simultaneous connect
-            feature allows two clients to connect without either of them
-            entering the listening state.  This feature of the TCP specification
-            is claimed to enable an attacker to deny the target access to a given
-            server by guessing the source port the target would use to make the
-            connection.
+      restartIfChanged = false;
 
-            This option is OFF by default because TCP simultaneous connect has
-            some legitimate uses.  Enable this option if you know what this TCP
-            feature is for and know that you do not need it.
-          '';
-        };
+      script = ''
+        if ${pkgs.gnugrep}/bin/grep -Fq 0 ${grsecLockPath} ; then
+          echo -n 1 > ${grsecLockPath}
+        fi
+      '';
 
-        verboseVersion = mkOption {
-          type = types.bool;
-          default = false;
-          description = "Use verbose version in kernel localversion.";
-        };
+      unitConfig = {
+        ConditionPathIsReadWrite = grsecLockPath;
+        DefaultDependencies = false;
+      } // optionalAttrs cfg.lockTunables {
+        OnFailure = "emergency.target";
+      };
 
-        kernelExtraConfig = mkOption {
-          type = types.str;
-          default = "";
-          description = "Extra kernel configuration parameters.";
-        };
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
       };
     };
-  };
 
-  config = mkIf cfg.enable {
-    assertions =
-      [
-        { assertion = (cfg.config.restrictProc -> !cfg.config.restrictProcWithGroup) ||
-                      (cfg.config.restrictProcWithGroup -> !cfg.config.restrictProc);
-          message   = "You cannot enable both restrictProc and restrictProcWithGroup";
-        }
-        { assertion = config.boot.kernelPackages.kernel.features ? grsecurity
-                   && config.boot.kernelPackages.kernel.features.grsecurity;
-          message = "grsecurity enabled, but kernel doesn't have grsec support";
-        }
-        { assertion = (cfg.config.mode == "auto" && (cfg.config.virtualisationConfig != null)) ->
-              cfg.config.hardwareVirtualisation != null;
-          message   = "when using auto grsec mode with virtualisation, you must specify if your hardware has virtualisation extensions";
-        }
-        { assertion = (cfg.config.mode == "auto" && (cfg.config.virtualisationConfig != null)) ->
-              cfg.config.virtualisationSoftware != null;
-         message   = "grsecurity configured for virtualisation but no virtualisation software specified";
-        }
-      ];
-
-    security.grsecurity.kernelPatch = lib.mkDefault pkgs.kernelPatches.grsecurity_latest;
-
-    systemd.services.grsec-lock = mkIf cfg.config.sysctl {
-      description     = "grsecurity sysctl-lock Service";
-      wants           = [ "systemd-sysctl.service" ];
-      after           = [ "systemd-sysctl.service" ];
-      wantedBy        = [ "multi-user.target" ];
-      serviceConfig.Type = "oneshot";
-      serviceConfig.RemainAfterExit = "yes";
-      unitConfig.ConditionPathIsReadWrite = "/proc/sys/kernel/grsecurity/grsec_lock";
-      script = ''
-        locked=`cat /proc/sys/kernel/grsecurity/grsec_lock`
-        if [ "$locked" == "0" ]; then
-            echo 1 > /proc/sys/kernel/grsecurity/grsec_lock
-            echo grsecurity sysctl lock - enabled
-        else
-            echo grsecurity sysctl lock already enabled - doing nothing
-        fi
-      '';
+    # Configure system tunables
+    boot.kernel.sysctl = {
+      # Removed under grsecurity
+      "kernel.kptr_restrict" = mkForce null;
+    } // optionalAttrs config.nix.useSandbox {
+      # chroot(2) restrictions that conflict with sandboxed Nix builds
+      "kernel.grsecurity.chroot_caps" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_chroot" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_pivot" = mkForce 0;
+    } // optionalAttrs config.boot.enableContainers {
+      # chroot(2) restrictions that conflict with NixOS lightweight containers
+      "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
+      "kernel.grsecurity.chroot_restrict_nice" = mkForce 0;
     };
 
-#   systemd.services.grsec-learn = {
-#     description     = "grsecurity learning Service";
-#     wantedBy        = [ "local-fs.target" ];
-#     serviceConfig   = {
-#       Type = "oneshot";
-#       RemainAfterExit = "yes";
-#       ExecStart = "${pkgs.gradm}/sbin/gradm -VFL /etc/grsec/learning.logs";
-#       ExecStop  = "${pkgs.gradm}/sbin/gradm -D";
-#     };
-#   };
-
-    system.activationScripts = lib.optionalAttrs (!cfg.config.disableRBAC) { grsec = ''
-      mkdir -p /etc/grsec
-      if [ ! -f /etc/grsec/learn_config ]; then
-        cp ${pkgs.gradm}/etc/grsec/learn_config /etc/grsec
-      fi
-      if [ ! -f /etc/grsec/policy ]; then
-        cp ${pkgs.gradm}/etc/grsec/policy /etc/grsec
-      fi
-      chmod -R 0600 /etc/grsec
-    ''; };
+    assertions = [
+      { assertion = !zfsNeededForBoot;
+        message = "grsecurity is currently incompatible with ZFS";
+      }
+    ];
 
-    # Enable AppArmor, gradm udev rules, and utilities
-    security.apparmor.enable   = true;
-    boot.kernelPackages        = customGrsecPkg;
-    services.udev.packages     = lib.optional (!cfg.config.disableRBAC) pkgs.gradm;
-    environment.systemPackages = [ pkgs.paxctl pkgs.pax-utils ] ++ lib.optional (!cfg.config.disableRBAC) pkgs.gradm;
   };
 }
diff --git a/nixos/modules/services/audio/squeezelite.nix b/nixos/modules/services/audio/squeezelite.nix
new file mode 100644
index 000000000000..f1a60be992d8
--- /dev/null
+++ b/nixos/modules/services/audio/squeezelite.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  uid = config.ids.uids.squeezelite;
+  cfg = config.services.squeezelite;
+
+in {
+
+  ###### interface
+
+  options = {
+
+    services.squeezelite= {
+
+      enable = mkEnableOption "Squeezelite, a software Squeezebox emulator";
+
+      dataDir = mkOption {
+        default = "/var/lib/squeezelite";
+        type = types.str;
+        description = ''
+          The directory where Squeezelite stores its name file.
+        '';
+      };
+
+      extraArguments = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Additional command line arguments to pass to Squeezelite.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.squeezelite= {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "sound.target" ];
+      description = "Software Squeezebox emulator";
+      preStart = "mkdir -p ${cfg.dataDir} && chown -R squeezelite ${cfg.dataDir}";
+      serviceConfig = {
+        ExecStart = "${pkgs.squeezelite}/bin/squeezelite -N ${cfg.dataDir}/player-name ${cfg.extraArguments}";
+        User = "squeezelite";
+        PermissionsStartOnly = true;
+      };
+    };
+
+    users.extraUsers.squeezelite= {
+      inherit uid;
+      group = "nogroup";
+      extraGroups = [ "audio" ];
+      description = "Squeezelite user";
+      home = "${cfg.dataDir}";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix
new file mode 100644
index 000000000000..b1449882b04f
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix
@@ -0,0 +1,100 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.buildkite-agent;
+  configFile = pkgs.writeText "buildkite-agent.cfg"
+    ''
+      token="${cfg.token}"
+      name="${cfg.name}"
+      meta-data="${cfg.meta-data}"
+      hooks-path="${pkgs.buildkite-agent}/share/hooks"
+      build-path="/var/lib/buildkite-agent/builds"
+      bootstrap-script="${pkgs.buildkite-agent}/share/bootstrap.sh"
+    '';
+in
+
+{
+  options = {
+    services.buildkite-agent = {
+      enable = mkEnableOption "buildkite-agent";
+
+      token = mkOption {
+        type = types.str;
+        description = ''
+          The token from your Buildkite "Agents" page.
+        '';
+      };
+
+      name = mkOption {
+        type = types.str;
+        description = ''
+          The name of the agent.
+        '';
+      };
+
+      meta-data = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Meta data for the agent.
+        '';
+      };
+
+      openssh =
+        { privateKey = mkOption {
+            type = types.str;
+            description = ''
+              Private agent key.
+            '';
+          };
+          publicKey = mkOption {
+            type = types.str;
+            description = ''
+              Public agent key.
+            '';
+          };
+        };
+    };
+  };
+
+  config = mkIf config.services.buildkite-agent.enable {
+    users.extraUsers.buildkite-agent =
+      { name = "buildkite-agent";
+        home = "/var/lib/buildkite-agent";
+        createHome = true;
+        description = "Buildkite agent user";
+      };
+
+    environment.systemPackages = [ pkgs.buildkite-agent ];
+
+    systemd.services.buildkite-agent =
+      { description = "Buildkite Agent";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        environment.HOME = "/var/lib/buildkite-agent";
+        preStart = ''
+            ${pkgs.coreutils}/bin/mkdir -m 0700 -p /var/lib/buildkite-agent/.ssh
+
+            if ! [ -f /var/lib/buildkite-agent/.ssh/id_rsa ]; then
+              echo "${cfg.openssh.privateKey}" > /var/lib/buildkite-agent/.ssh/id_rsa
+              ${pkgs.coreutils}/bin/chmod 600 /var/lib/buildkite-agent/.ssh/id_rsa
+            fi
+
+            if ! [ -f /var/lib/buildkite-agent/.ssh/id_rsa.pub ]; then
+              echo "${cfg.openssh.publicKey}" > /var/lib/buildkite-agent/.ssh/id_rsa.pub
+              ${pkgs.coreutils}/bin/chmod 600 /var/lib/buildkite-agent/.ssh/id_rsa.pub
+            fi
+          '';
+
+        serviceConfig =
+          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config ${configFile}";
+            User = "buildkite-agent";
+            RestartSec = 5;
+            Restart = "on-failure";
+            TimeoutSec = 10;
+          };
+      };
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index ae3f186c1d0a..7da49fa0aaab 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -154,7 +154,7 @@ in {
       '';
 
       script = ''
-        ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins} --httpListenAddress=${cfg.listenAddress} \
+        ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins}/lib/jenkins.war --httpListenAddress=${cfg.listenAddress} \
                                                   --httpPort=${toString cfg.port} \
                                                   --prefix=${cfg.prefix} \
                                                   ${concatStringsSep " " cfg.extraOptions}
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index cbdc676d47bd..9f22aa7c92b2 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -40,6 +40,13 @@ in
         description = "Group account under which slapd runs.";
       };
 
+      urlList = mkOption {
+        type = types.listOf types.string;
+        default = [ "ldap:///" ];
+        description = "URL list slapd should listen on.";
+        example = [ "ldaps:///" ];
+      };
+
       dataDir = mkOption {
         type = types.string;
         default = "/var/db/openldap";
@@ -50,7 +57,7 @@ in
         type = types.lines;
         default = "";
         description = "
-          sldapd.conf configuration
+          slapd.conf configuration
         ";
         example = literalExample ''
             '''
@@ -89,7 +96,7 @@ in
         mkdir -p ${cfg.dataDir}
         chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}
       '';
-      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -f ${configFile}";
+      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" -f ${configFile}";
     };
 
     users.extraUsers.openldap =
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
new file mode 100644
index 000000000000..43b4219c51dd
--- /dev/null
+++ b/nixos/modules/services/editors/emacs.nix
@@ -0,0 +1,86 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.emacs;
+
+  editorScript = pkgs.writeScriptBin "emacseditor" ''
+    #!${pkgs.stdenv.shell}
+    if [ -z "$1" ]; then
+      exec ${cfg.package}/bin/emacsclient --create-frame --alternate-editor ${cfg.package}/bin/emacs
+    else
+      exec ${cfg.package}/bin/emacsclient --alternate-editor ${cfg.package}/bin/emacs "$@"
+    fi
+  '';
+
+in {
+
+  options.services.emacs = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = ''
+        Whether to enable a user service for the Emacs daemon. Use <literal>emacsclient</literal> to connect to the
+        daemon. If <literal>true</literal>, <varname>services.emacs.install</varname> is
+        considered <literal>true</literal>, whatever its value.
+      '';
+    };
+
+    install = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = ''
+        Whether to install a user service for the Emacs daemon. Once
+        the service is started, use emacsclient to connect to the
+        daemon.
+
+        The service must be manually started for each user with
+        "systemctl --user start emacs" or globally through
+        <varname>services.emacs.enable</varname>.
+      '';
+    };
+
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.emacs;
+      defaultText = "pkgs.emacs";
+      description = ''
+        emacs derivation to use.
+      '';
+    };
+
+    defaultEditor = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = ''
+        When enabled, configures emacsclient to be the default editor
+        using the EDITOR environment variable.
+      '';
+    };
+  };
+
+  config = mkIf (cfg.enable || cfg.install) {
+    systemd.user.services.emacs = {
+      description = "Emacs: the extensible, self-documenting text editor";
+
+      serviceConfig = {
+        Type      = "forking";
+        ExecStart = "${pkgs.bash}/bin/bash -c 'source ${config.system.build.setEnvironment}; exec ${cfg.package}/bin/emacs --daemon'";
+        ExecStop  = "${cfg.package}/bin/emacsclient --eval (kill-emacs)";
+        Restart   = "always";
+      };
+    } // optionalAttrs cfg.enable { wantedBy = [ "default.target" ]; };
+
+    environment.systemPackages = [ cfg.package editorScript ];
+
+    environment.variables = if cfg.defaultEditor then {
+      EDITOR = mkOverride 900 "${editorScript}/bin/emacseditor";
+    } else {};
+  };
+}
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 10f3daea69e4..90834c5b2605 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -78,7 +78,7 @@ in
           test -e ${stateDir}/saves/${cfg.saveName}.zip || \
             ${pkgs.factorio-headless}/bin/factorio         \
               --config=${cfg.configFile}                   \
-              --create=${cfg.saveName}
+              --create=${stateDir}/saves/${cfg.saveName}.zip
       '';
 
       serviceConfig = {
@@ -93,7 +93,7 @@ in
           "${pkgs.factorio-headless}/bin/factorio"
           "--config=${cfg.configFile}"
           "--port=${toString cfg.port}"
-          "--start-server=${cfg.saveName}"
+          "--start-server=${stateDir}/saves/${cfg.saveName}.zip"
         ];
       };
     };
diff --git a/nixos/modules/services/hardware/tlp.nix b/nixos/modules/services/hardware/tlp.nix
index 6427c5be6818..5ab7daafa85b 100644
--- a/nixos/modules/services/hardware/tlp.nix
+++ b/nixos/modules/services/hardware/tlp.nix
@@ -54,6 +54,9 @@ in
 
   config = mkIf cfg.enable {
 
+    powerManagement.scsiLinkPolicy = null;
+    powerManagement.cpuFreqGovernor = null;
+
     systemd.services = {
       tlp = {
         description = "TLP system startup/shutdown";
@@ -61,6 +64,7 @@ in
         after = [ "multi-user.target" ];
         wantedBy = [ "multi-user.target" ];
         before = [ "shutdown.target" ];
+        restartTriggers = [ confFile ];
 
         serviceConfig = {
           Type = "oneshot";
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index e019e6c3f237..2c3016d27df8 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -105,7 +105,7 @@ in
             prune {
               whitelist_names => [
                 "type", "@timestamp", "@version",
-                "MESSAGE", "PRIORITY", "SYSLOG_FACILITY",
+                "MESSAGE", "PRIORITY", "SYSLOG_FACILITY"
               ]
             }
           }
diff --git a/nixos/modules/services/mail/opendkim.nix b/nixos/modules/services/mail/opendkim.nix
index af996758f41f..f065208ddfc1 100644
--- a/nixos/modules/services/mail/opendkim.nix
+++ b/nixos/modules/services/mail/opendkim.nix
@@ -101,7 +101,7 @@ in {
       wantedBy = [ "multi-user.target" ];
 
       serviceConfig = {
-        ExecStart = "${pkgs.opendkim}/bin/opendkim ${concatMapStringsSep " " escapeShellArg args}";
+        ExecStart = "${pkgs.opendkim}/bin/opendkim ${escapeShellArgs args}";
         User = cfg.user;
         Group = cfg.group;
         RuntimeDirectory = optional (cfg.socket == defaultSock) "opendkim";
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index bad9d527f9a1..cdde41446224 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -127,11 +127,11 @@ let
     #               (yes)   (yes)   (no)    (never) (100)
     # ==========================================================================
     smtp      inet  n       -       n       -       -       smtpd
-    #submission inet n       -       n       -       -       smtpd
-    #  -o smtpd_tls_security_level=encrypt
-    #  -o smtpd_sasl_auth_enable=yes
-    #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
-    #  -o milter_macro_daemon_name=ORIGINATING
+  '' + optionalString cfg.enableSubmission ''
+    submission inet n       -       n       -       -       smtpd
+      ${concatStringsSep "\n  " (mapAttrsToList (x: y: "-o " + x + "=" + y) cfg.submissionOptions)}
+  ''
+  + ''
     pickup    unix  n       -       n       60      1       pickup
     cleanup   unix  n       -       n       -       0       cleanup
     qmgr      unix  n       -       n       300     1       qmgr
@@ -201,6 +201,28 @@ in
         default = true;
         description = "Whether to enable smtp in master.cf.";
       };
+      
+      enableSubmission = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable smtp submission";
+      };
+
+      submissionOptions = mkOption {
+        type = types.attrs;
+        default = { "smtpd_tls_security_level" = "encrypt";
+                    "smtpd_sasl_auth_enable" = "yes";
+                    "smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
+                    "milter_macro_daemon_name" = "ORIGINATING";
+                  };
+        description = "Options for the submission config in master.cf";
+        example = { "smtpd_tls_security_level" = "encrypt";
+                    "smtpd_sasl_auth_enable" = "yes";
+                    "smtpd_sasl_type" = "dovecot";
+                    "smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
+                    "milter_macro_daemon_name" = "ORIGINATING";
+                  };
+      };
 
       setSendmail = mkOption {
         type = types.bool;
diff --git a/nixos/modules/services/misc/confd.nix b/nixos/modules/services/misc/confd.nix
index c0fbf06e6c4c..72ec68dee6b3 100644
--- a/nixos/modules/services/misc/confd.nix
+++ b/nixos/modules/services/misc/confd.nix
@@ -75,7 +75,7 @@ in {
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/confd";
+        ExecStart = "${cfg.package.bin}/bin/confd";
       };
     };
 
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index 218802e0cf00..e5a125ad3245 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -36,49 +36,32 @@ in
         default = false;
         description = "Whether to enable the DisnixWebService interface running on Apache Tomcat";
       };
-
-      publishInfrastructure = {
-        enable = mkOption {
-          default = false;
-          description = "Whether to publish capabilities/properties of this machine in as attributes in the infrastructure option";
-        };
-
-        enableAuthentication = mkOption {
-          default = false;
-          description = "Whether to publish authentication credentials through the infrastructure attribute (not recommended in combination with Avahi)";
-        };
-      };
-
-      infrastructure = mkOption {
-        default = {};
-        description = "List of name value pairs containing properties for the infrastructure model";
-      };
-
-      publishAvahi = mkOption {
-        default = false;
-        description = "Whether to publish capabilities/properties as a Disnix service through Avahi";
+      
+      package = mkOption {
+        type = types.path;
+        description = "The Disnix package";
+        default = pkgs.disnix;
       };
 
     };
 
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.disnix pkgs.dysnomia ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
+    dysnomia.enable = true;
+    
+    environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
     services.dbus.enable = true;
     services.dbus.packages = [ pkgs.disnix ];
 
-    services.avahi.enable = cfg.publishAvahi;
-
     services.tomcat.enable = cfg.useWebServiceInterface;
     services.tomcat.extraGroups = [ "disnix" ];
     services.tomcat.javaOpts = "${optionalString cfg.useWebServiceInterface "-Djava.library.path=${pkgs.libmatthew_java}/lib/jni"} ";
     services.tomcat.sharedLibs = optional cfg.useWebServiceInterface "${pkgs.DisnixWebService}/share/java/DisnixConnection.jar"
-                                 ++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar";
+      ++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar";
     services.tomcat.webapps = optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
     users.extraGroups = singleton
@@ -86,38 +69,6 @@ in
         gid = config.ids.gids.disnix;
       };
 
-    services.disnix.infrastructure =
-      optionalAttrs (cfg.publishInfrastructure.enable)
-      ( { hostname = config.networking.hostName;
-          #targetHost = config.deployment.targetHost;
-          system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
-
-          supportedTypes = (import "${pkgs.stdenv.mkDerivation {
-            name = "supportedtypes";
-            buildCommand = ''
-              ( echo -n "[ "
-                cd ${dysnomia}/libexec/dysnomia
-                for i in *
-                do
-                    echo -n "\"$i\" "
-                done
-                echo -n " ]") > $out
-            '';
-          }}");
-        }
-        #// optionalAttrs (cfg.useWebServiceInterface) { targetEPR = "http://${config.deployment.targetHost}:8080/DisnixWebService/services/DisnixWebService"; }
-        // optionalAttrs (config.services.httpd.enable) { documentRoot = config.services.httpd.documentRoot; }
-        // optionalAttrs (config.services.mysql.enable) { mysqlPort = config.services.mysql.port; }
-        // optionalAttrs (config.services.tomcat.enable) { tomcatPort = 8080; }
-        // optionalAttrs (config.services.svnserve.enable) { svnBaseDir = config.services.svnserve.svnBaseDir; }
-        // optionalAttrs (config.services.ejabberd.enable) { ejabberdUser = config.services.ejabberd.user; }
-        // optionalAttrs (cfg.publishInfrastructure.enableAuthentication) (
-          optionalAttrs (config.services.mysql.enable) { mysqlUsername = "root"; mysqlPassword = readFile config.services.mysql.rootPassword; })
-        )
-    ;
-
-    services.disnix.publishInfrastructure.enable = cfg.publishAvahi;
-
     systemd.services = {
       disnix = {
         description = "Disnix server";
@@ -133,46 +84,17 @@ in
 
         restartIfChanged = false;
 
-        path = [ pkgs.nix pkgs.disnix dysnomia "/run/current-system/sw" ];
+        path = [ config.nix.package cfg.package config.dysnomia.package "/run/current-system/sw" ];
 
         environment = {
           HOME = "/root";
-        };
-
-        preStart = ''
-          mkdir -p /etc/systemd-mutable/system
-          if [ ! -f /etc/systemd-mutable/system/dysnomia.target ]
-          then
-              ( echo "[Unit]"
-                echo "Description=Services that are activated and deactivated by Dysnomia"
-                echo "After=final.target"
-              ) > /etc/systemd-mutable/system/dysnomia.target
-          fi
-        '';
-
-        script = "disnix-service";
+        }
+        // (if config.environment.variables ? DYSNOMIA_CONTAINERS_PATH then { inherit (config.environment.variables) DYSNOMIA_CONTAINERS_PATH; } else {})
+        // (if config.environment.variables ? DYSNOMIA_MODULES_PATH then { inherit (config.environment.variables) DYSNOMIA_MODULES_PATH; } else {});
+        
+        serviceConfig.ExecStart = "${cfg.package}/bin/disnix-service";
       };
-    } // optionalAttrs cfg.publishAvahi {
-      disnixAvahi = {
-        description = "Disnix Avahi publisher";
-        wants = [ "avahi-daemon.service" ];
-        wantedBy = [ "multi-user.target" ];
 
-        script = ''
-          ${pkgs.avahi}/bin/avahi-publish-service disnix-${config.networking.hostName} _disnix._tcp 22 \
-            "mem=$(grep 'MemTotal:' /proc/meminfo | sed -e 's/kB//' -e 's/MemTotal://' -e 's/ //g')" \
-            ${concatMapStrings (infrastructureAttrName:
-              let infrastructureAttrValue = getAttr infrastructureAttrName (cfg.infrastructure);
-              in
-              if isInt infrastructureAttrValue then
-              ''${infrastructureAttrName}=${toString infrastructureAttrValue} \
-              ''
-              else
-              ''${infrastructureAttrName}=\"${infrastructureAttrValue}\" \
-              ''
-              ) (attrNames (cfg.infrastructure))}
-        '';
-      };
     };
   };
 }
diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix
new file mode 100644
index 000000000000..df44d0a54866
--- /dev/null
+++ b/nixos/modules/services/misc/dysnomia.nix
@@ -0,0 +1,217 @@
+{pkgs, lib, config, ...}:
+
+with lib;
+
+let
+  cfg = config.dysnomia;
+  
+  printProperties = properties:
+    concatMapStrings (propertyName:
+      let
+        property = properties."${propertyName}";
+      in
+      if isList property then "${propertyName}=(${lib.concatMapStrings (elem: "\"${toString elem}\" ") (properties."${propertyName}")})\n"
+      else "${propertyName}=\"${toString property}\"\n"
+    ) (builtins.attrNames properties);
+  
+  properties = pkgs.stdenv.mkDerivation {
+    name = "dysnomia-properties";
+    buildCommand = ''
+      cat > $out << "EOF"
+      ${printProperties cfg.properties}
+      EOF
+    '';
+  };
+  
+  containersDir = pkgs.stdenv.mkDerivation {
+    name = "dysnomia-containers";
+    buildCommand = ''
+      mkdir -p $out
+      cd $out
+      
+      ${concatMapStrings (containerName:
+        let
+          containerProperties = cfg.containers."${containerName}";
+        in
+        ''
+          cat > ${containerName} <<EOF
+          ${printProperties containerProperties}
+          type=${containerName}
+          EOF
+        ''
+      ) (builtins.attrNames cfg.containers)}
+    '';
+  };
+  
+  linkMutableComponents = {containerName}:
+    ''
+      mkdir ${containerName}
+      
+      ${concatMapStrings (componentName:
+        let
+          component = cfg.components."${containerName}"."${componentName}";
+        in
+        "ln -s ${component} ${containerName}/${componentName}\n"
+      ) (builtins.attrNames (cfg.components."${containerName}" or {}))}
+    '';
+  
+  componentsDir = pkgs.stdenv.mkDerivation {
+    name = "dysnomia-components";
+    buildCommand = ''
+      mkdir -p $out
+      cd $out
+      
+      ${concatMapStrings (containerName:
+        let
+          components = cfg.components."${containerName}";
+        in
+        linkMutableComponents { inherit containerName; }
+      ) (builtins.attrNames cfg.components)}
+    '';
+  };
+in
+{
+  options = {
+    dysnomia = {
+      
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable Dysnomia";
+      };
+      
+      enableAuthentication = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to publish privacy-sensitive authentication credentials";
+      };
+      
+      package = mkOption {
+        type = types.path;
+        description = "The Dysnomia package";
+      };
+      
+      properties = mkOption {
+        description = "An attribute set in which each attribute represents a machine property. Optionally, these values can be shell substitutions.";
+        default = {};
+      };
+      
+      containers = mkOption {
+        description = "An attribute set in which each key represents a container and each value an attribute set providing its configuration properties";
+        default = {};
+      };
+      
+      components = mkOption {
+        description = "An atttribute set in which each key represents a container and each value an attribute set in which each key represents a component and each value a derivation constructing its initial state";
+        default = {};
+      };
+      
+      extraContainerProperties = mkOption {
+        description = "An attribute set providing additional container settings in addition to the default properties";
+        default = {};
+      };
+      
+      extraContainerPaths = mkOption {
+        description = "A list of paths containing additional container configurations that are added to the search folders";
+        default = [];
+      };
+      
+      extraModulePaths = mkOption {
+        description = "A list of paths containing additional modules that are added to the search folders";
+        default = [];
+      };
+    };
+  };
+  
+  config = mkIf cfg.enable {
+  
+    environment.etc = {
+      "dysnomia/containers" = {
+        source = containersDir;
+      };
+      "dysnomia/components" = {
+        source = componentsDir;
+      };
+      "dysnomia/properties" = {
+        source = properties;
+      };
+    };
+    
+    environment.variables = {
+      DYSNOMIA_STATEDIR = "/var/state/dysnomia-nixos";
+      DYSNOMIA_CONTAINERS_PATH = "${lib.concatMapStrings (containerPath: "${containerPath}:") cfg.extraContainerPaths}/etc/dysnomia/containers";
+      DYSNOMIA_MODULES_PATH = "${lib.concatMapStrings (modulePath: "${modulePath}:") cfg.extraModulePaths}/etc/dysnomia/modules";
+    };
+    
+    environment.systemPackages = [ cfg.package ];
+    
+    dysnomia.package = pkgs.dysnomia.override (origArgs: {
+      enableApacheWebApplication = config.services.httpd.enable;
+      enableAxis2WebService = config.services.tomcat.axis2.enable;
+      enableEjabberdDump = config.services.ejabberd.enable;
+      enableMySQLDatabase = config.services.mysql.enable;
+      enablePostgreSQLDatabase = config.services.postgresql.enable;
+      enableSubversionRepository = config.services.svnserve.enable;
+      enableTomcatWebApplication = config.services.tomcat.enable;
+      enableMongoDatabase = config.services.mongodb.enable;
+    });
+    
+    dysnomia.properties = {
+      hostname = config.networking.hostName;
+      system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
+
+      supportedTypes = (import "${pkgs.stdenv.mkDerivation {
+        name = "supportedtypes";
+        buildCommand = ''
+          ( echo -n "[ "
+            cd ${cfg.package}/libexec/dysnomia
+            for i in *
+            do
+                echo -n "\"$i\" "
+            done
+            echo -n " ]") > $out
+        '';
+      }}");
+    };
+    
+    dysnomia.containers = lib.recursiveUpdate ({
+      process = {};
+      wrapper = {};
+    }
+    // lib.optionalAttrs (config.services.httpd.enable) { apache-webapplication = {
+      documentRoot = config.services.httpd.documentRoot;
+    }; }
+    // lib.optionalAttrs (config.services.tomcat.axis2.enable) { axis2-webservice = {}; }
+    // lib.optionalAttrs (config.services.ejabberd.enable) { ejabberd-dump = {
+      ejabberdUser = config.services.ejabberd.user;
+    }; }
+    // lib.optionalAttrs (config.services.mysql.enable) { mysql-database = {
+        mysqlPort = config.services.mysql.port;
+      } // lib.optionalAttrs cfg.enableAuthentication {
+        mysqlUsername = "root";
+        mysqlPassword = builtins.readFile (config.services.mysql.rootPassword);
+      };
+    }
+    // lib.optionalAttrs (config.services.postgresql.enable && cfg.enableAuthentication) { postgresql-database = {
+      postgresqlUsername = "root";
+    }; }
+    // lib.optionalAttrs (config.services.tomcat.enable) { tomcat-webapplication = {
+      tomcatPort = 8080;
+    }; }
+    // lib.optionalAttrs (config.services.mongodb.enable) { mongo-database = {}; }
+    // lib.optionalAttrs (config.services.svnserve.enable) { subversion-repository = {
+      svnBaseDir = config.services.svnserve.svnBaseDir;
+    }; }) cfg.extraContainerProperties;
+
+    system.activationScripts.dysnomia = ''
+      mkdir -p /etc/systemd-mutable/system
+      if [ ! -f /etc/systemd-mutable/system/dysnomia.target ]
+      then
+          ( echo "[Unit]"
+            echo "Description=Services that are activated and deactivated by Dysnomia"
+            echo "After=final.target"
+          ) > /etc/systemd-mutable/system/dysnomia.target
+      fi
+    '';
+  };
+}
diff --git a/nixos/modules/services/misc/etcd.nix b/nixos/modules/services/misc/etcd.nix
index bc8064e3c879..0d6ed8eb9043 100644
--- a/nixos/modules/services/misc/etcd.nix
+++ b/nixos/modules/services/misc/etcd.nix
@@ -115,7 +115,7 @@ in {
 
       serviceConfig = {
         Type = "notify";
-        ExecStart = "${pkgs.etcd}/bin/etcd";
+        ExecStart = "${pkgs.etcd.bin}/bin/etcd";
         User = "etcd";
         PermissionsStartOnly = true;
       };
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index 0ae0516769c0..1a95e2d9367d 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -5,17 +5,31 @@ with lib;
 let
   cfg = config.services.matrix-synapse;
   logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
+  mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${if r.compress then "true" else "false"}}'';
+  mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${if l.tls then "true" else "false"}, x_forwarded: ${if l.x_forwarded then "true" else "false"}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
   configFile = pkgs.writeText "homeserver.yaml" ''
 tls_certificate_path: "${cfg.tls_certificate_path}"
+${optionalString (cfg.tls_private_key_path != null) ''
 tls_private_key_path: "${cfg.tls_private_key_path}"
+''}
 tls_dh_params_path: "${cfg.tls_dh_params_path}"
 no_tls: ${if cfg.no_tls then "true" else "false"}
+${optionalString (cfg.bind_port != null) ''
 bind_port: ${toString cfg.bind_port}
+''}
+${optionalString (cfg.unsecure_port != null) ''
 unsecure_port: ${toString cfg.unsecure_port}
+''}
+${optionalString (cfg.bind_host != null) ''
 bind_host: "${cfg.bind_host}"
+''}
 server_name: "${cfg.server_name}"
 pid_file: "/var/run/matrix-synapse.pid"
 web_client: ${if cfg.web_client then "true" else "false"}
+${optionalString (cfg.public_baseurl != null) ''
+public_baseurl: "${cfg.public_baseurl}"
+''}
+listeners: [${concatStringsSep "," (map mkListener cfg.listeners)}]
 database: {
   name: "${cfg.database_type}",
   args: {
@@ -24,21 +38,41 @@ database: {
     )}
   }
 }
+event_cache_size: "${cfg.event_cache_size}"
+verbose: ${cfg.verbose}
 log_file: "/var/log/matrix-synapse/homeserver.log"
 log_config: "${logConfigFile}"
+rc_messages_per_second: ${cfg.rc_messages_per_second}
+rc_message_burst_count: ${cfg.rc_message_burst_count}
+federation_rc_window_size: ${cfg.federation_rc_window_size}
+federation_rc_sleep_limit: ${cfg.federation_rc_sleep_limit}
+federation_rc_sleep_delay: ${cfg.federation_rc_sleep_delay}
+federation_rc_reject_limit: ${cfg.federation_rc_reject_limit}
+federation_rc_concurrent: ${cfg.federation_rc_concurrent}
 media_store_path: "/var/lib/matrix-synapse/media"
+uploads_path: "/var/lib/matrix-synapse/uploads"
+max_upload_size: "${cfg.max_upload_size}"
+max_image_pixels: "${cfg.max_image_pixels}"
+dynamic_thumbnails: ${if cfg.dynamic_thumbnails then "true" else "false"}
+url_preview_enabled: False
 recaptcha_private_key: "${cfg.recaptcha_private_key}"
 recaptcha_public_key: "${cfg.recaptcha_public_key}"
 enable_registration_captcha: ${if cfg.enable_registration_captcha then "true" else "false"}
-turn_uris: ${if (length cfg.turn_uris) == 0 then "[]" else ("\n" + (concatStringsSep "\n" (map (s: "- " + s) cfg.turn_uris)))}
+turn_uris: ${builtins.toJSON cfg.turn_uris}
 turn_shared_secret: "${cfg.turn_shared_secret}"
 enable_registration: ${if cfg.enable_registration then "true" else "false"}
-${optionalString (cfg.registration_shared_secret != "") ''
+${optionalString (cfg.registration_shared_secret != null) ''
 registration_shared_secret: "${cfg.registration_shared_secret}"
 ''}
+recaptcha_siteverify_api: "https://www.google.com/recaptcha/api/siteverify"
+turn_user_lifetime: "${cfg.turn_user_lifetime}"
+user_creation_max_duration: ${cfg.user_creation_max_duration}
+bcrypt_rounds: ${cfg.bcrypt_rounds}
+allow_guest_access: {if cfg.allow_guest_access then "true" else "false"}
 enable_metrics: ${if cfg.enable_metrics then "true" else "false"}
 report_stats: ${if cfg.report_stats then "true" else "false"}
 signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key"
+key_refresh_interval: "${cfg.key_refresh_interval}"
 perspectives:
   servers: {
     ${concatStringsSep "},\n" (mapAttrsToList (n: v: ''
@@ -52,6 +86,8 @@ perspectives:
     '') cfg.servers)}
     }
   }
+app_service_config_files: ${builtins.toJSON cfg.app_service_config_files}
+
 ${cfg.extraConfig}
 '';
 in {
@@ -73,53 +109,65 @@ in {
           Don't bind to the https port
         '';
       };
-      tls_certificate_path = mkOption {
-        type = types.path;
-        default = "/var/lib/matrix-synapse/homeserver.tls.crt";
+      bind_port = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        example = 8448;
         description = ''
-          PEM encoded X509 certificate for TLS
+          DEPRECATED: Use listeners instead.
+          The port to listen for HTTPS requests on.
+          For when matrix traffic is sent directly to synapse.
         '';
       };
-      tls_private_key_path = mkOption {
-        type = types.path;
-        default = "/var/lib/matrix-synapse/homeserver.tls.key";
+      unsecure_port = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        example = 8008;
         description = ''
-          PEM encoded private key for TLS
+          DEPRECATED: Use listeners instead.
+          The port to listen for HTTP requests on.
+          For when matrix traffic passes through loadbalancer that unwraps TLS.
         '';
       };
-      tls_dh_params_path = mkOption {
-        type = types.path;
-        default = "/var/lib/matrix-synapse/homeserver.tls.dh";
+      bind_host = mkOption {
+        type = types.nullOr types.str;
+        default = null;
         description = ''
-          PEM dh parameters for ephemeral keys
+          DEPRECATED: Use listeners instead.
+          Local interface to listen on.
+          The empty string will cause synapse to listen on all interfaces.
         '';
       };
-      bind_port = mkOption {
-        type = types.int;
-        default = 8448;
+      tls_certificate_path = mkOption {
+        type = types.str;
+        default = "/var/lib/matrix-synapse/homeserver.tls.crt";
         description = ''
-          The port to listen for HTTPS requests on.
-          For when matrix traffic is sent directly to synapse.
+          PEM encoded X509 certificate for TLS.
+          You can replace the self-signed certificate that synapse
+          autogenerates on launch with your own SSL certificate + key pair
+          if you like.  Any required intermediary certificates can be
+          appended after the primary certificate in hierarchical order.
         '';
       };
-      unsecure_port = mkOption {
-        type = types.int;
-        default = 8008;
+      tls_private_key_path = mkOption {
+        type = types.nullOr types.str;
+        default = "/var/lib/matrix-synapse/homeserver.tls.key";
+        example = null;
         description = ''
-          The port to listen for HTTP requests on.
-          For when matrix traffic passes through loadbalancer that unwraps TLS.
+          PEM encoded private key for TLS. Specify null if synapse is not
+          speaking TLS directly.
         '';
       };
-      bind_host = mkOption {
+      tls_dh_params_path = mkOption {
         type = types.str;
-        default = "";
+        default = "/var/lib/matrix-synapse/homeserver.tls.dh";
         description = ''
-          Local interface to listen on.
-          The empty string will cause synapse to listen on all interfaces.
+          PEM dh parameters for ephemeral keys
         '';
       };
       server_name = mkOption {
         type = types.str;
+        example = "example.com";
         description = ''
           The domain name of the server, with optional explicit port.
           This is used by remote servers to connect to this server,
@@ -134,6 +182,145 @@ in {
           Whether to serve a web client from the HTTP/HTTPS root resource.
         '';
       };
+      public_baseurl = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "https://example.com:8448/";
+        description = ''
+          The public-facing base URL for the client API (not including _matrix/...)
+        '';
+      };
+      listeners = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            port = mkOption {
+              type = types.int;
+              example = 8448;
+              description = ''
+                The port to listen for HTTP(S) requests on.
+              '';
+            };
+            bind_address = mkOption {
+              type = types.str;
+              default = "";
+              example = "203.0.113.42";
+              description = ''
+                Local interface to listen on.
+                The empty string will cause synapse to listen on all interfaces.
+              '';
+            };
+            type = mkOption {
+              type = types.str;
+              default = "http";
+              description = ''
+                Type of listener.
+              '';
+            };
+            tls = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether to listen for HTTPS connections rather than HTTP.
+              '';
+            };
+            x_forwarded = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Use the X-Forwarded-For (XFF) header as the client IP and not the
+                actual client IP.
+              '';
+            };
+            resources = mkOption {
+              type = types.listOf (types.submodule {
+                options = {
+                  names = mkOption {
+                    type = types.listOf types.str;
+                    description = ''
+                      List of resources to host on this listener.
+                    '';
+                    example = ["client" "webclient" "federation"];
+                  };
+                  compress = mkOption {
+                    type = types.bool;
+                    description = ''
+                      Should synapse compress HTTP responses to clients that support it?
+                      This should be disabled if running synapse behind a load balancer
+                      that can do automatic compression.
+                    '';
+                  };
+                };
+              });
+              description = ''
+                List of HTTP resources to serve on this listener.
+              '';
+            };
+          };
+        });
+        default = [{
+          port = 8448;
+          bind_address = "";
+          type = "http";
+          tls = true;
+          x_forwarded = false;
+          resources = [
+            { names = ["client" "webclient"]; compress = true; }
+            { names = ["federation"]; compress = false; }
+          ];
+        }];
+        description = ''
+          List of ports that Synapse should listen on, their purpose and their configuration.
+        '';
+      };
+      verbose = mkOption {
+        type = types.str;
+        default = "0";
+        description = "Logging verbosity level.";
+      };
+      rc_messages_per_second = mkOption {
+        type = types.str;
+        default = "0.2";
+        description = "Number of messages a client can send per second";
+      };
+      rc_message_burst_count = mkOption {
+        type = types.str;
+        default = "10.0";
+        description = "Number of message a client can send before being throttled";
+      };
+      federation_rc_window_size = mkOption {
+        type = types.str;
+        default = "1000";
+        description = "The federation window size in milliseconds";
+      };
+      federation_rc_sleep_limit = mkOption {
+        type = types.str;
+        default = "10";
+        description = ''
+          The number of federation requests from a single server in a window
+          before the server will delay processing the request.
+        '';
+      };
+      federation_rc_sleep_delay = mkOption {
+        type = types.str;
+        default = "500";
+        description = ''
+          The duration in milliseconds to delay processing events from
+          remote servers by if they go over the sleep limit.
+        '';
+      };
+      federation_rc_reject_limit = mkOption {
+        type = types.str;
+        default = "50";
+        description = ''
+          The maximum number of concurrent federation requests allowed
+          from a single server
+        '';
+      };
+      federation_rc_concurrent = mkOption {
+        type = types.str;
+        default = "3";
+        description = "The number of federation requests to concurrently process from a single server";
+      };
       database_type = mkOption {
         type = types.enum [ "sqlite3" "psycopg2" ];
         default = "sqlite3";
@@ -150,6 +337,11 @@ in {
           Arguments to pass to the engine.
         '';
       };
+      event_cache_size = mkOption {
+        type = types.str;
+        default = "10K";
+        description = "Number of events to cache in memory.";
+      };
       recaptcha_private_key = mkOption {
         type = types.str;
         default = "";
@@ -187,6 +379,11 @@ in {
           The shared secret used to compute passwords for the TURN server
         '';
       };
+      turn_user_lifetime = mkOption {
+        type = types.str;
+        default = "1h";
+        description = "How long generated TURN credentials last";
+      };
       enable_registration = mkOption {
         type = types.bool;
         default = false;
@@ -195,8 +392,8 @@ in {
         '';
       };
       registration_shared_secret = mkOption {
-        type = types.str;
-        default = "";
+        type = types.nullOr types.str;
+        default = null;
         description = ''
           If set, allows registration by anyone who also has the shared
           secret, even if registration is otherwise disabled.
@@ -216,7 +413,7 @@ in {
         '';
       };
       servers = mkOption {
-        type = types.attrs;
+        type = types.attrsOf (types.attrsOf types.str);
         default = {
           "matrix.org" = {
             "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
@@ -226,6 +423,69 @@ in {
           The trusted servers to download signing keys from.
         '';
       };
+      max_upload_size = mkOption {
+        type = types.str;
+        default = "10M";
+        description = "The largest allowed upload size in bytes";
+      };
+      max_image_pixels = mkOption {
+        type = types.str;
+        default = "32M";
+        description = "Maximum number of pixels that will be thumbnailed";
+      };
+      dynamic_thumbnails = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to generate new thumbnails on the fly to precisely match
+          the resolution requested by the client. If true then whenever
+          a new resolution is requested by the client the server will
+          generate a new thumbnail. If false the server will pick a thumbnail
+          from a precalculated list.
+        '';
+      };
+      user_creation_max_duration = mkOption {
+        type = types.str;
+        default = "1209600000";
+        description = ''
+          Sets the expiry for the short term user creation in
+          milliseconds. The default value is two weeks.
+        '';
+      };
+      bcrypt_rounds = mkOption {
+        type = types.str;
+        default = "12";
+        description = ''
+          Set the number of bcrypt rounds used to generate password hash.
+          Larger numbers increase the work factor needed to generate the hash.
+        '';
+      };
+      allow_guest_access = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Allows users to register as guests without a password/email/etc, and
+          participate in rooms hosted on this server which have been made
+          accessible to anonymous users.
+        '';
+      };
+      key_refresh_interval = mkOption {
+        type = types.str;
+        default = "1d";
+        description = ''
+          How long key response published by this server is valid for.
+          Used to set the valid_until_ts in /key/v2 APIs.
+          Determines how quickly servers will query to check which keys
+          are still valid.
+        '';
+      };
+      app_service_config_files = mkOption {
+        type = types.listOf types.path;
+        default = [ ];
+        description = ''
+          A list of application service config file to use
+        '';
+      };
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -265,7 +525,7 @@ in {
         mkdir -p /var/lib/matrix-synapse
         chmod 700 /var/lib/matrix-synapse
         chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse
-        ${cfg.package}/bin/homeserver --config-path ${configFile} --generate-keys
+        ${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse/ --generate-keys
       '';
       serviceConfig = {
         Type = "simple";
diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix
index ea97d6e30e83..0844190a5490 100644
--- a/nixos/modules/services/misc/parsoid.nix
+++ b/nixos/modules/services/misc/parsoid.nix
@@ -91,7 +91,7 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.nodePackages_0_10.parsoid}/lib/node_modules/parsoid/api/server.js -c ${confFile} -n ${toString cfg.workers}";
+        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/api/server.js -c ${confFile} -n ${toString cfg.workers}";
       };
     };
 
diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index b7d14e90a2b7..c846ffd04551 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -152,8 +152,6 @@ let
     };
   };
 
-  mkShellStr = val: "'${replaceStrings ["'"] ["'\\''"] val}'";
-
   certtool = "${pkgs.gnutls.bin}/bin/certtool";
 
   nixos-taskserver = pkgs.buildPythonPackage {
diff --git a/nixos/modules/services/monitoring/bosun.nix b/nixos/modules/services/monitoring/bosun.nix
index 51d38e8db4de..9a1e790d3ab6 100644
--- a/nixos/modules/services/monitoring/bosun.nix
+++ b/nixos/modules/services/monitoring/bosun.nix
@@ -148,7 +148,7 @@ in {
         User = cfg.user;
         Group = cfg.group;
         ExecStart = ''
-          ${cfg.package}/bin/bosun -c ${configFile}
+          ${cfg.package.bin}/bin/bosun -c ${configFile}
         '';
       };
     };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index defbd9289dcd..0b7f3ce0a29b 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -228,7 +228,7 @@ in {
       after = ["networking.target"];
       environment = mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/grafana-server -homepath ${cfg.dataDir}";
+        ExecStart = "${cfg.package.bin}/bin/grafana-server -homepath ${cfg.dataDir}";
         WorkingDirectory = cfg.dataDir;
         User = "grafana";
       };
diff --git a/nixos/modules/services/monitoring/scollector.nix b/nixos/modules/services/monitoring/scollector.nix
index 1e397435e600..2684482c6184 100644
--- a/nixos/modules/services/monitoring/scollector.nix
+++ b/nixos/modules/services/monitoring/scollector.nix
@@ -119,7 +119,7 @@ in {
         PermissionsStartOnly = true;
         User = cfg.user;
         Group = cfg.group;
-        ExecStart = "${cfg.package}/bin/scollector -conf=${conf} ${lib.concatStringsSep " " cfg.extraOpts}";
+        ExecStart = "${cfg.package.bin}/bin/scollector -conf=${conf} ${lib.concatStringsSep " " cfg.extraOpts}";
       };
     };
 
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 8b178ee93980..7650f45c5570 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -24,6 +24,7 @@ let
     use-ipv4=${if ipv4 then "yes" else "no"}
     use-ipv6=${if ipv6 then "yes" else "no"}
     ${optionalString (interfaces!=null) "allow-interfaces=${concatStringsSep "," interfaces}"}
+    ${optionalString (domainName!=null) "domain-name=${domainName}"}
 
     [wide-area]
     enable-wide-area=${if wideArea then "yes" else "no"}
@@ -65,6 +66,14 @@ in
         '';
       };
 
+      domainName = mkOption {
+        type = types.str;
+        default = "local";
+        description = ''
+          Domain name for all advertisements.
+        '';
+      };
+
       browseDomains = mkOption {
         default = [ "0pointer.de" "zeroconf.org" ];
         description = ''
diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix
index 2aa101f980da..166ee7732375 100644
--- a/nixos/modules/services/networking/consul.nix
+++ b/nixos/modules/services/networking/consul.nix
@@ -178,14 +178,14 @@ in
             (filterAttrs (n: _: hasPrefix "consul.d/" n) config.environment.etc);
 
         serviceConfig = {
-          ExecStart = "@${cfg.package}/bin/consul consul agent -config-dir /etc/consul.d"
+          ExecStart = "@${cfg.package.bin}/bin/consul consul agent -config-dir /etc/consul.d"
             + concatMapStrings (n: " -config-file ${n}") configFiles;
-          ExecReload = "${cfg.package}/bin/consul reload";
+          ExecReload = "${cfg.package.bin}/bin/consul reload";
           PermissionsStartOnly = true;
           User = if cfg.dropPrivileges then "consul" else null;
           TimeoutStartSec = "0";
         } // (optionalAttrs (cfg.leaveOnStop) {
-          ExecStop = "${cfg.package}/bin/consul leave";
+          ExecStop = "${cfg.package.bin}/bin/consul leave";
         });
 
         path = with pkgs; [ iproute gnugrep gawk consul ];
@@ -236,7 +236,7 @@ in
 
         serviceConfig = {
           ExecStart = ''
-            ${cfg.alerts.package}/bin/consul-alerts start \
+            ${cfg.alerts.package.bin}/bin/consul-alerts start \
               --alert-addr=${cfg.alerts.listenAddr} \
               --consul-addr=${cfg.alerts.consulAddr} \
               ${optionalString cfg.alerts.watchChecks "--watch-checks"} \
diff --git a/nixos/modules/services/networking/coturn.nix b/nixos/modules/services/networking/coturn.nix
new file mode 100644
index 000000000000..14e6932d868b
--- /dev/null
+++ b/nixos/modules/services/networking/coturn.nix
@@ -0,0 +1,327 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.coturn;
+  pidfile = "/run/turnserver/turnserver.pid";
+  configFile = pkgs.writeText "turnserver.conf" ''
+listening-port=${toString cfg.listening-port}
+tls-listening-port=${toString cfg.tls-listening-port}
+alt-listening-port=${toString cfg.alt-listening-port}
+alt-tls-listening-port=${toString cfg.alt-tls-listening-port}
+${concatStringsSep "\n" (map (x: "listening-ip=${x}") cfg.listening-ips)}
+${concatStringsSep "\n" (map (x: "relay-ip=${x}") cfg.relay-ips)}
+min-port=${toString cfg.min-port}
+max-port=${toString cfg.max-port}
+${lib.optionalString cfg.lt-cred-mech "lt-cred-mech"}
+${lib.optionalString cfg.no-auth "no-auth"}
+${lib.optionalString cfg.use-auth-secret "use-auth-secret"}
+${lib.optionalString (cfg.static-auth-secret != null) ("static-auth-secret=${cfg.static-auth-secret}")}
+realm=${cfg.realm}
+${lib.optionalString cfg.no-udp "no-udp"}
+${lib.optionalString cfg.no-tcp "no-tcp"}
+${lib.optionalString cfg.no-tls "no-tls"}
+${lib.optionalString cfg.no-dtls "no-dtls"}
+${lib.optionalString cfg.no-udp-relay "no-udp-relay"}
+${lib.optionalString cfg.no-tcp-relay "no-tcp-relay"}
+${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"}
+${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"}
+${lib.optionalString (cfg.dh-file != null) ("dh-file=${cfg.dh-file}")}
+no-stdout-log
+syslog
+pidfile=${pidfile}
+${lib.optionalString cfg.secure-stun "secure-stun"}
+${lib.optionalString cfg.no-cli "no-cli"}
+cli-ip=${cfg.cli-ip}
+cli-port=${toString cfg.cli-port}
+${lib.optionalString (cfg.cli-password != null) ("cli-password=${cfg.cli-password}")}
+${cfg.extraConfig}
+'';
+in {
+  options = {
+    services.coturn = {
+      enable = mkEnableOption "coturn TURN server";
+      listening-port = mkOption {
+        type = types.int;
+        default = 3478;
+        description = ''
+          TURN listener port for UDP and TCP.
+          Note: actually, TLS and DTLS sessions can connect to the
+          "plain" TCP and UDP port(s), too - if allowed by configuration.
+        '';
+      };
+      tls-listening-port = mkOption {
+        type = types.int;
+        default = 5349;
+        description = ''
+          TURN listener port for TLS.
+          Note: actually, "plain" TCP and UDP sessions can connect to the TLS and
+          DTLS port(s), too - if allowed by configuration. The TURN server
+          "automatically" recognizes the type of traffic. Actually, two listening
+          endpoints (the "plain" one and the "tls" one) are equivalent in terms of
+          functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
+          For secure TCP connections, we currently support SSL version 3 and
+          TLS version 1.0, 1.1 and 1.2.
+          For secure UDP connections, we support DTLS version 1.
+        '';
+      };
+      alt-listening-port = mkOption {
+        type = types.int;
+        default = cfg.listening-port + 1;
+        defaultText = "listening-port + 1";
+        description = ''
+          Alternative listening port for UDP and TCP listeners;
+          default (or zero) value means "listening port plus one".
+          This is needed for RFC 5780 support
+          (STUN extension specs, NAT behavior discovery). The TURN Server
+          supports RFC 5780 only if it is started with more than one
+          listening IP address of the same family (IPv4 or IPv6).
+          RFC 5780 is supported only by UDP protocol, other protocols
+          are listening to that endpoint only for "symmetry".
+        '';
+      };
+      alt-tls-listening-port = mkOption {
+        type = types.int;
+        default = cfg.tls-listening-port + 1;
+        defaultText = "tls-listening-port + 1";
+        description = ''
+          Alternative listening port for TLS and DTLS protocols.
+        '';
+      };
+      listening-ips = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "203.0.113.42" "2001:DB8::42" ];
+        description = ''
+          Listener IP addresses of relay server.
+          If no IP(s) specified in the config file or in the command line options,
+          then all IPv4 and IPv6 system IPs will be used for listening.
+        '';
+      };
+      relay-ips = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "203.0.113.42" "2001:DB8::42" ];
+        description = ''
+          Relay address (the local IP address that will be used to relay the
+          packets to the peer).
+          Multiple relay addresses may be used.
+          The same IP(s) can be used as both listening IP(s) and relay IP(s).
+
+          If no relay IP(s) specified, then the turnserver will apply the default
+          policy: it will decide itself which relay addresses to be used, and it
+          will always be using the client socket IP address as the relay IP address
+          of the TURN session (if the requested relay address family is the same
+          as the family of the client socket).
+        '';
+      };
+      min-port = mkOption {
+        type = types.int;
+        default = 49152;
+        description = ''
+          Lower bound of UDP relay endpoints
+        '';
+      };
+      max-port = mkOption {
+        type = types.int;
+        default = 65535;
+        description = ''
+          Upper bound of UDP relay endpoints
+        '';
+      };
+      lt-cred-mech = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use long-term credential mechanism.
+        '';
+      };
+      no-auth = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          This option is opposite to lt-cred-mech.
+          (TURN Server with no-auth option allows anonymous access).
+          If neither option is defined, and no users are defined,
+          then no-auth is default. If at least one user is defined,
+          in this file or in command line or in usersdb file, then
+          lt-cred-mech is default.
+        '';
+      };
+      use-auth-secret = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          TURN REST API flag.
+          Flag that sets a special authorization option that is based upon authentication secret.
+          This feature can be used with the long-term authentication mechanism, only.
+          This feature purpose is to support "TURN Server REST API", see
+          "TURN REST API" link in the project's page
+          https://github.com/coturn/coturn/
+
+          This option is used with timestamp:
+
+          usercombo -> "timestamp:userid"
+          turn user -> usercombo
+          turn password -> base64(hmac(secret key, usercombo))
+
+          This allows TURN credentials to be accounted for a specific user id.
+          If you don't have a suitable id, the timestamp alone can be used.
+          This option is just turning on secret-based authentication.
+          The actual value of the secret is defined either by option static-auth-secret,
+          or can be found in the turn_secret table in the database.
+        '';
+      };
+      static-auth-secret = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          'Static' authentication secret value (a string) for TURN REST API only.
+          If not set, then the turn server
+          will try to use the 'dynamic' value in turn_secret table
+          in user database (if present). The database-stored  value can be changed on-the-fly
+          by a separate program, so this is why that other mode is 'dynamic'.
+        '';
+      };
+      realm = mkOption {
+        type = types.str;
+        default = config.networking.hostName;
+        example = "example.com";
+        description = ''
+          The default realm to be used for the users when no explicit
+          origin/realm relationship was found in the database, or if the TURN
+          server is not using any database (just the commands-line settings
+          and the userdb file). Must be used with long-term credentials
+          mechanism or with TURN REST API.
+        '';
+      };
+      cert = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/acme/example.com/fullchain.pem";
+        description = ''
+          Certificate file in PEM format.
+        '';
+      };
+      pkey = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/acme/example.com/key.pem";
+        description = ''
+          Private key file in PEM format.
+        '';
+      };
+      dh-file = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Use custom DH TLS key, stored in PEM format in the file.
+        '';
+      };
+      secure-stun = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Require authentication of the STUN Binding request.
+          By default, the clients are allowed anonymous access to the STUN Binding functionality.
+        '';
+      };
+      no-cli = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Turn OFF the CLI support.
+        '';
+      };
+      cli-ip = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          Local system IP address to be used for CLI server endpoint.
+        '';
+      };
+      cli-port = mkOption {
+        type = types.int;
+        default = 5766;
+        description = ''
+          CLI server port.
+        '';
+      };
+      cli-password = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          CLI access password.
+          For the security reasons, it is recommended to use the encrypted
+          for of the password (see the -P command in the turnadmin utility).
+        '';
+      };
+      no-udp = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable UDP client listener";
+      };
+      no-tcp = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable TCP client listener";
+      };
+      no-tls = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable TLS client listener";
+      };
+      no-dtls = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable DTLS client listener";
+      };
+      no-udp-relay = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable UDP relay endpoints";
+      };
+      no-tcp-relay = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable TCP relay endpoints";
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional configuration options";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers = [
+      { name = "turnserver";
+        uid = config.ids.uids.turnserver;
+        description = "coturn TURN server user";
+      } ];
+    users.extraGroups = [
+      { name = "turnserver";
+        gid = config.ids.gids.turnserver;
+        members = [ "turnserver" ];
+      } ];
+
+    systemd.services.coturn = {
+      description = "coturn TURN server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      unitConfig = {
+        Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)";
+      };
+
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.coturn}/bin/turnserver -c ${configFile}";
+        RuntimeDirectory = "turnserver";
+        User = "turnserver";
+        Group = "turnserver";
+        Restart = "on-abort";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index c5dd1e71c189..633ceaebfa4e 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -7,22 +7,8 @@ let
 
   stateDir = "/var/spool/ddclient";
   ddclientUser = "ddclient";
-  ddclientFlags = "-foreground -verbose -noquiet -file ${ddclientCfg}";
+  ddclientFlags = "-foreground -verbose -noquiet -file /etc/ddclient.conf";
   ddclientPIDFile = "${stateDir}/ddclient.pid";
-  ddclientCfg = pkgs.writeText "ddclient.conf" ''
-    daemon=600
-    cache=${stateDir}/ddclient.cache
-    pid=${ddclientPIDFile}
-    use=${config.services.ddclient.use}
-    login=${config.services.ddclient.username}
-    password=${config.services.ddclient.password}
-    protocol=${config.services.ddclient.protocol}
-    server=${config.services.ddclient.server}
-    ssl=${if config.services.ddclient.ssl then "yes" else "no"}
-    wildcard=YES
-    ${config.services.ddclient.domain}
-    ${config.services.ddclient.extraConfig}
-  '';
 
 in
 
@@ -122,10 +108,30 @@ in
       home = stateDir;
     };
 
+    environment.etc."ddclient.conf" = {
+      uid = config.ids.uids.ddclient;
+      mode = "0600";
+      text = ''
+        daemon=600
+        cache=${stateDir}/ddclient.cache
+        pid=${ddclientPIDFile}
+        use=${config.services.ddclient.use}
+        login=${config.services.ddclient.username}
+        password=${config.services.ddclient.password}
+        protocol=${config.services.ddclient.protocol}
+        server=${config.services.ddclient.server}
+        ssl=${if config.services.ddclient.ssl then "yes" else "no"}
+        wildcard=YES
+        ${config.services.ddclient.domain}
+        ${config.services.ddclient.extraConfig}
+      '';
+    };
+
     systemd.services.ddclient = {
       description = "Dynamic DNS Client";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
+      restartTriggers = [ config.environment.etc."ddclient.conf".source ];
 
       serviceConfig = {
         # Uncomment this if too many problems occur:
diff --git a/nixos/modules/services/networking/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix
index 9868f303ab2b..8ecc16257db8 100644
--- a/nixos/modules/services/networking/ejabberd.nix
+++ b/nixos/modules/services/networking/ejabberd.nix
@@ -13,7 +13,7 @@ let
 
   ectl = ''${cfg.package}/bin/ejabberdctl ${if cfg.configFile == null then "" else "--config ${cfg.configFile}"} --ctl-config "${ctlcfg}" --spool "${cfg.spoolDir}" --logs "${cfg.logsDir}"'';
 
-  dumps = lib.concatMapStringsSep " " lib.escapeShellArg cfg.loadDumps;
+  dumps = lib.escapeShellArgs cfg.loadDumps;
 
 in {
 
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 9912ad9ae3fc..8aac1360526f 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -231,11 +231,6 @@ in {
     users.extraUsers = [{
       name = "nm-openvpn";
       uid = config.ids.uids.nm-openvpn;
-    }
-    {
-      # to enable link-local connections
-      name = "avahi-autoipd";
-      uid = config.ids.uids.avahi-autoipd;
     }];
 
     systemd.packages = cfg.packages;
diff --git a/nixos/modules/services/networking/offlineimap.nix b/nixos/modules/services/networking/offlineimap.nix
new file mode 100644
index 000000000000..31ce9280f319
--- /dev/null
+++ b/nixos/modules/services/networking/offlineimap.nix
@@ -0,0 +1,73 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.offlineimap;
+in {
+
+  options.services.offlineimap = {
+    enable = mkEnableOption "Offlineimap, a software to dispose your mailbox(es) as a local Maildir(s).";
+
+    install = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = ''
+        Whether to install a user service for Offlineimap. Once
+        the service is started, emails will be fetched automatically.
+
+        The service must be manually started for each user with
+        "systemctl --user start offlineimap" or globally through
+        <varname>services.offlineimap.enable</varname>.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.offlineimap;
+      defaultText = "pkgs.offlineimap";
+      description = "Offlineimap derivation to use.";
+    };
+
+    path = mkOption {
+      type = types.listOf types.path;
+      default = [];
+      example = literalExample "[ pkgs.pass pkgs.bash pkgs.notmuch ]";
+      description = "List of derivations to put in Offlineimap's path.";
+    };
+
+    onCalendar = mkOption {
+      type = types.str;
+      default = "*:0/3"; # every 3 minutes
+      description = "How often is offlineimap started. Default is '*:0/3' meaning every 3 minutes. See systemd.time(7) for more information about the format.";
+    };
+
+    timeoutStartSec = mkOption {
+      type = types.str;
+      default = "120sec"; # Kill if still alive after 2 minutes
+      description = "How long waiting for offlineimap before killing it. Default is '120sec' meaning every 2 minutes. See systemd.time(7) for more information about the format.";
+    };
+  };
+  config = mkIf (cfg.enable || cfg.install) {
+    systemd.user.services.offlineimap = {
+      description = "Offlineimap: a software to dispose your mailbox(es) as a local Maildir(s)";
+      serviceConfig = {
+        Type      = "oneshot";
+        ExecStart = "${cfg.package}/bin/offlineimap -u basic -o -1";
+        TimeoutStartSec = cfg.timeoutStartSec;
+      };
+      path = cfg.path;
+    };
+    environment.systemPackages = [ "${cfg.package}" ];
+    systemd.user.timers.offlineimap = {
+      description = "offlineimap timer";
+      timerConfig               = {
+        Unit = "offlineimap.service";
+        OnCalendar = cfg.onCalendar;
+        # start immediately after computer is started:
+        Persistent = "true";
+      };
+    } // optionalAttrs cfg.enable { wantedBy = [ "default.target" ]; };
+  };
+}
diff --git a/nixos/modules/services/networking/pptpd.nix b/nixos/modules/services/networking/pptpd.nix
new file mode 100644
index 000000000000..513e6174752c
--- /dev/null
+++ b/nixos/modules/services/networking/pptpd.nix
@@ -0,0 +1,124 @@
+{ config, stdenv, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    services.pptpd = {
+      enable = mkEnableOption "Whether pptpd should be run on startup.";
+
+      serverIp = mkOption {
+        type        = types.string;
+        description = "The server-side IP address.";
+        default     = "10.124.124.1";
+      };
+
+      clientIpRange = mkOption {
+        type        = types.string;
+        description = "The range from which client IPs are drawn.";
+        default     = "10.124.124.2-11";
+      };
+
+      maxClients = mkOption {
+        type        = types.int;
+        description = "The maximum number of simultaneous connections.";
+        default     = 10;
+      };
+
+      extraPptpdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pptpd configuration file.";
+        default     = "";
+      };
+
+      extraPppdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pppd options file.";
+        default     = "";
+        example     = ''
+          ms-dns 8.8.8.8
+          ms-dns 8.8.4.4
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.pptpd.enable {
+    systemd.services.pptpd = let
+      cfg = config.services.pptpd;
+
+      pptpd-conf = pkgs.writeText "pptpd.conf" ''
+        # Inspired from pptpd-1.4.0/samples/pptpd.conf
+        ppp ${ppp-pptpd-wrapped}/bin/pppd
+        option ${pppd-options}
+        pidfile /run/pptpd.pid
+        localip ${cfg.serverIp}
+        remoteip ${cfg.clientIpRange}
+        connections ${toString cfg.maxClients} # (Will get harmless warning if inconsistent with IP range)
+
+        # Extra
+        ${cfg.extraPptpdOptions}
+      '';
+
+      pppd-options = pkgs.writeText "ppp-options-pptpd.conf" ''
+        # From: cat pptpd-1.4.0/samples/options.pptpd | grep -v ^# | grep -v ^$
+        name pptpd
+        refuse-pap
+        refuse-chap
+        refuse-mschap
+        require-mschap-v2
+        require-mppe-128
+        proxyarp
+        lock
+        nobsdcomp
+        novj
+        novjccomp
+        nologfd
+
+        # Extra:
+        ${cfg.extraPppdOptions}
+      '';
+
+      ppp-pptpd-wrapped = pkgs.stdenv.mkDerivation {
+        name         = "ppp-pptpd-wrapped";
+        phases       = [ "installPhase" ];
+        buildInputs  = with pkgs; [ makeWrapper ];
+        installPhase = ''
+          mkdir -p $out/bin
+          makeWrapper ${pkgs.ppp}/bin/pppd $out/bin/pppd \
+            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+            --set NIX_REDIRECTS "/etc/ppp=/etc/ppp-pptpd"
+        '';
+      };
+    in {
+      description = "pptpd server";
+
+      requires = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -p -m 700 /etc/ppp-pptpd
+
+        secrets="/etc/ppp-pptpd/chap-secrets"
+
+        [ -f "$secrets" ] || cat > "$secrets" << EOF
+        # From: pptpd-1.4.0/samples/chap-secrets
+        # Secrets for authentication using CHAP
+        # client	server	secret		IP addresses
+        #username	pptpd	password	*
+        EOF
+
+        chown root.root "$secrets"
+        chmod 600 "$secrets"
+      '';
+
+      serviceConfig = {
+        ExecStart = "${pkgs.pptpd}/bin/pptpd --conf ${pptpd-conf}";
+        KillMode  = "process";
+        Restart   = "on-success";
+        Type      = "forking";
+        PIDFile   = "/run/pptpd.pid";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/shairport-sync.nix b/nixos/modules/services/networking/shairport-sync.nix
index a523e66d09b9..908de9efd6fb 100644
--- a/nixos/modules/services/networking/shairport-sync.nix
+++ b/nixos/modules/services/networking/shairport-sync.nix
@@ -52,6 +52,8 @@ in
   config = mkIf config.services.shairport-sync.enable {
 
     services.avahi.enable = true;
+    services.avahi.publish.enable = true;
+    services.avahi.publish.userServices = true;
 
     users.extraUsers = singleton
       { name = cfg.user;
diff --git a/nixos/modules/services/networking/skydns.nix b/nixos/modules/services/networking/skydns.nix
index 39ebaa45a794..ba913482e3cb 100644
--- a/nixos/modules/services/networking/skydns.nix
+++ b/nixos/modules/services/networking/skydns.nix
@@ -83,7 +83,7 @@ in {
         SKYDNS_NAMESERVERS = concatStringsSep "," cfg.nameservers;
       };
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/skydns";
+        ExecStart = "${cfg.package.bin}/bin/skydns";
       };
     };
 
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 53648aef1e04..8d22c10d3f78 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -129,7 +129,7 @@ in {
       in {
         description = "WPA Supplicant";
 
-        after = [ "network-interfaces.target" ];
+        after = [ "network-interfaces.target" ] ++ lib.concatMap deviceUnit ifaces;
         requires = lib.concatMap deviceUnit ifaces;
         wantedBy = [ "network.target" ];
 
diff --git a/nixos/modules/services/networking/xl2tpd.nix b/nixos/modules/services/networking/xl2tpd.nix
new file mode 100644
index 000000000000..5e006c13f0d0
--- /dev/null
+++ b/nixos/modules/services/networking/xl2tpd.nix
@@ -0,0 +1,143 @@
+{ config, stdenv, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    services.xl2tpd = {
+      enable = mkEnableOption "Whether xl2tpd should be run on startup.";
+
+      serverIp = mkOption {
+        type        = types.string;
+        description = "The server-side IP address.";
+        default     = "10.125.125.1";
+      };
+
+      clientIpRange = mkOption {
+        type        = types.string;
+        description = "The range from which client IPs are drawn.";
+        default     = "10.125.125.2-11";
+      };
+
+      extraXl2tpOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the xl2tpd configuration file.";
+        default     = "";
+      };
+
+      extraPppdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pppd options file.";
+        default     = "";
+        example     = ''
+          ms-dns 8.8.8.8
+          ms-dns 8.8.4.4
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.xl2tpd.enable {
+    systemd.services.xl2tpd = let
+      cfg = config.services.xl2tpd;
+
+      # Config files from https://help.ubuntu.com/community/L2TPServer
+      xl2tpd-conf = pkgs.writeText "xl2tpd.conf" ''
+        [global]
+        ipsec saref = no
+
+        [lns default]
+        local ip = ${cfg.serverIp}
+        ip range = ${cfg.clientIpRange}
+        pppoptfile = ${pppd-options}
+        length bit = yes
+
+        ; Extra
+        ${cfg.extraXl2tpOptions}
+      '';
+
+      pppd-options = pkgs.writeText "ppp-options-xl2tpd.conf" ''
+        refuse-pap
+        refuse-chap
+        refuse-mschap
+        require-mschap-v2
+        # require-mppe-128
+        asyncmap 0
+        auth
+        crtscts
+        idle 1800
+        mtu 1200
+        mru 1200
+        lock
+        hide-password
+        local
+        # debug
+        name xl2tpd
+        # proxyarp
+        lcp-echo-interval 30
+        lcp-echo-failure 4
+
+        # Extra:
+        ${cfg.extraPppdOptions}
+      '';
+
+      xl2tpd-ppp-wrapped = pkgs.stdenv.mkDerivation {
+        name         = "xl2tpd-ppp-wrapped";
+        phases       = [ "installPhase" ];
+        buildInputs  = with pkgs; [ makeWrapper ];
+        installPhase = ''
+          mkdir -p $out/bin
+
+          makeWrapper ${pkgs.ppp}/sbin/pppd $out/bin/pppd \
+            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+            --set NIX_REDIRECTS "/etc/ppp=/etc/xl2tpd/ppp"
+
+          makeWrapper ${pkgs.xl2tpd}/bin/xl2tpd $out/bin/xl2tpd \
+            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+            --set NIX_REDIRECTS "${pkgs.ppp}/sbin/pppd=$out/bin/pppd"
+        '';
+      };
+    in {
+      description = "xl2tpd server";
+
+      requires = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -p -m 700 /etc/xl2tpd
+
+        pushd /etc/xl2tpd > /dev/null
+
+        mkdir -p -m 700 ppp
+
+        [ -f ppp/chap-secrets ] || cat > ppp/chap-secrets << EOF
+        # Secrets for authentication using CHAP
+        # client	server	secret		IP addresses
+        #username	xl2tpd	password	*
+        EOF
+
+        chown root.root ppp/chap-secrets
+        chmod 600 ppp/chap-secrets
+
+        # The documentation says this file should be present but doesn't explain why and things work even if not there:
+        [ -f l2tp-secrets ] || (echo -n "* * "; ${pkgs.apg}/bin/apg -n 1 -m 32 -x 32 -a 1 -M LCN) > l2tp-secrets
+        chown root.root l2tp-secrets
+        chmod 600 l2tp-secrets
+
+        popd > /dev/null
+
+        mkdir -p /run/xl2tpd
+        chown root.root /run/xl2tpd
+        chmod 700       /run/xl2tpd
+      '';
+
+      serviceConfig = {
+        ExecStart = "${xl2tpd-ppp-wrapped}/bin/xl2tpd -D -c ${xl2tpd-conf} -s /etc/xl2tpd/l2tp-secrets -p /run/xl2tpd/pid -C /run/xl2tpd/control";
+        KillMode  = "process";
+        Restart   = "on-success";
+        Type      = "simple";
+        PIDFile   = "/run/xl2tpd/pid";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
index 17ac8fe7e245..9299aaac2f70 100644
--- a/nixos/modules/services/search/elasticsearch.nix
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -157,11 +157,14 @@ in {
 
     environment.systemPackages = [ cfg.package ];
 
-    users.extraUsers = singleton {
-      name = "elasticsearch";
-      uid = config.ids.uids.elasticsearch;
-      description = "Elasticsearch daemon user";
-      home = cfg.dataDir;
+    users = {
+      groups.elasticsearch.gid = config.ids.gids.elasticsearch;
+      users.elasticsearch = {
+        uid = config.ids.uids.elasticsearch;
+        description = "Elasticsearch daemon user";
+        home = cfg.dataDir;
+        group = "elasticsearch";
+      };
     };
   };
 }
diff --git a/nixos/modules/services/security/hologram.nix b/nixos/modules/services/security/hologram.nix
index 58f308df7a21..e267fed27955 100644
--- a/nixos/modules/services/security/hologram.nix
+++ b/nixos/modules/services/security/hologram.nix
@@ -95,7 +95,7 @@ in {
       wantedBy    = [ "multi-user.target" ];
 
       serviceConfig = {
-        ExecStart = "${pkgs.hologram}/bin/hologram-server --debug --conf ${cfgFile}";
+        ExecStart = "${pkgs.hologram.bin}/bin/hologram-server --debug --conf ${cfgFile}";
       };
     };
   };
diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix
index b149373076a6..4c20392214fc 100644
--- a/nixos/modules/services/security/oauth2_proxy.nix
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -515,7 +515,7 @@ in
       serviceConfig = {
         User = "oauth2_proxy";
         Restart = "always";
-        ExecStart = "${cfg.package}/bin/oauth2_proxy ${mkCommandLine cfg}";
+        ExecStart = "${cfg.package.bin}/bin/oauth2_proxy ${mkCommandLine cfg}";
       };
     };
 
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index 5583a1bcb3ab..8bcd6f01656d 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -65,10 +65,13 @@ in
         default = [ ];
         description = ''
           Packages whose D-Bus configuration files should be included in
-          the configuration of the D-Bus system-wide message bus.
-          Specifically, every file in
+          the configuration of the D-Bus system-wide or session-wide
+          message bus.  Specifically, files in the following directories
+          will be included into their respective DBus configuration paths:
           <filename><replaceable>pkg</replaceable>/etc/dbus-1/system.d</filename>
-          is included.
+          <filename><replaceable>pkg</replaceable>/share/dbus-1/system-services</filename>
+          <filename><replaceable>pkg</replaceable>/etc/dbus-1/session.d</filename>
+          <filename><replaceable>pkg</replaceable>/share/dbus-1/services</filename>
         '';
       };
 
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
index c23897192b4c..397857ea0858 100644
--- a/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -12,7 +12,9 @@ let
 
   httpdConf = mainCfg.configFile;
 
-  php = pkgs.php.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
+  php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
+
+  phpMajorVersion = head (splitString "." php.version);
 
   getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80;
 
@@ -337,7 +339,8 @@ let
         allModules =
           concatMap (svc: svc.extraModulesPre) allSubservices
           ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
-          ++ optional enablePHP { name = "php5"; path = "${php}/modules/libphp5.so"; }
+          ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
+          ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
           ++ concatMap (svc: svc.extraModules) allSubservices
           ++ extraForeignModules;
       in concatMapStrings load allModules
@@ -541,12 +544,27 @@ in
         '';
       };
 
+      enableMellon = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the mod_auth_mellon module.";
+      };
+
       enablePHP = mkOption {
         type = types.bool;
         default = false;
         description = "Whether to enable the PHP module.";
       };
 
+      phpPackage = mkOption {
+        type = types.package;
+        default = pkgs.php;
+        defaultText = "pkgs.php";
+        description = ''
+          Overridable attribute of the PHP package to use.
+        '';
+      };
+
       phpOptions = mkOption {
         type = types.lines;
         default = "";
@@ -650,6 +668,7 @@ in
 
         environment =
           optionalAttrs enablePHP { PHPRC = phpIni; }
+          // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH  = "${pkgs.xmlsec}/lib"; }
           // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
 
         preStart =
diff --git a/nixos/modules/services/web-servers/caddy.nix b/nixos/modules/services/web-servers/caddy.nix
index 0d2612aaa66b..b84431373bd1 100644
--- a/nixos/modules/services/web-servers/caddy.nix
+++ b/nixos/modules/services/web-servers/caddy.nix
@@ -33,7 +33,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.caddy}/bin/caddy -conf=${configFile} -email=${cfg.email}";
+        ExecStart = "${pkgs.caddy.bin}/bin/caddy -conf=${configFile} -email=${cfg.email}";
 	Type = "simple";
 	User = "caddy";
 	Group = "caddy";
diff --git a/nixos/modules/services/web-servers/lighttpd/inginious.nix b/nixos/modules/services/web-servers/lighttpd/inginious.nix
new file mode 100644
index 000000000000..43deccb6aef8
--- /dev/null
+++ b/nixos/modules/services/web-servers/lighttpd/inginious.nix
@@ -0,0 +1,262 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg = config.services.lighttpd.inginious;
+  inginious = pkgs.inginious;
+  execName = "inginious-${if cfg.useLTI then "lti" else "webapp"}";
+
+  inginiousConfigFile = if cfg.configFile != null then cfg.configFile else pkgs.writeText "inginious.yaml" ''
+    # Backend; can be:
+    # - "local" (run containers on the same machine)
+    # - "remote" (connect to distant docker daemon and auto start agents) (choose this if you use boot2docker)
+    # - "remote_manual" (connect to distant and manually installed agents)
+    backend: "${cfg.backendType}"
+
+    ## TODO (maybe): Add an option for the "remote" backend in this NixOS module.
+    # List of remote docker daemon to which the backend will try
+    # to connect (backend: remote only)
+    #docker_daemons:
+    #  - # Host of the docker daemon *from the webapp*
+    #    remote_host: "some.remote.server"
+    #    # Port of the distant docker daemon *from the webapp*
+    #    remote_docker_port: "2375"
+    #    # A mandatory port used by the backend and the agent that will be automatically started.
+    #    # Needs to be available on the remote host, and to be open in the firewall.
+    #    remote_agent_port: "63456"
+    #    # Does the remote docker requires tls? Defaults to false.
+    #    # Parameter can be set to true or path to the certificates
+    #    #use_tls: false
+    #    # Link to the docker daemon *from the host that runs the docker daemon*. Defaults to:
+    #    #local_location: "unix:///var/run/docker.sock"
+    #    # Path to the cgroups "mount" *from the host that runs the docker daemon*. Defaults to:
+    #    #cgroups_location: "/sys/fs/cgroup"
+    #    # Name that will be used to reference the agent
+    #    #"agent_name": "inginious-agent"
+
+    # List of remote agents to which the backend will try
+    # to connect (backend: remote_manual only)
+    # Example:
+    #agents:
+    #  - host: "192.168.59.103"
+    #    port: 5001
+    agents:
+    ${lib.concatMapStrings (agent:
+      "  - host: \"${agent.host}\"\n" +
+      "    port: ${agent.port}\n"
+    ) cfg.remoteAgents}
+
+    # Location of the task directory
+    tasks_directory: "${cfg.tasksDirectory}"
+
+    # Super admins: list of user names that can do everything in the backend
+    superadmins:
+    ${lib.concatMapStrings (x: "  - \"${x}\"\n") cfg.superadmins}
+
+    # Aliases for containers
+    # Only containers listed here can be used by tasks
+    containers:
+    ${lib.concatStrings (lib.mapAttrsToList (name: fullname:
+      "  ${name}: \"${fullname}\"\n"
+    ) cfg.containers)}
+
+    # Use single minified javascript file (production) or multiple files (dev) ?
+    use_minified_js: true
+
+    ## TODO (maybe): Add NixOS options for these parameters.
+
+    # MongoDB options
+    #mongo_opt:
+    #    host: localhost
+    #    database: INGInious
+
+    # Disable INGInious?
+    #maintenance: false
+
+    #smtp:
+    #    sendername: 'INGInious <no-reply@inginious.org>'
+    #    host: 'smtp.gmail.com'
+    #    port: 587
+    #    username: 'configme@gmail.com'
+    #    password: 'secret'
+    #    starttls: True
+
+    ## NixOS extra config
+
+    ${cfg.extraConfig}
+  '';
+in
+{
+  options.services.lighttpd.inginious = {
+    enable = mkEnableOption  "INGInious, an automated code testing and grading system.";
+
+    configFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = literalExample ''pkgs.writeText "configuration.yaml" "# custom config options ...";'';
+      description = ''The path to an INGInious configuration file.'';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        # Load the dummy auth plugin.
+        plugins:
+          - plugin_module: inginious.frontend.webapp.plugins.auth.demo_auth
+            users:
+              # register the user "test" with the password "someverycomplexpassword"
+              test: someverycomplexpassword
+      '';
+      description = ''Extra option in YaML format, to be appended to the config file.'';
+    };
+
+    tasksDirectory = mkOption {
+      type = types.path;
+      default = "${inginious}/lib/python2.7/site-packages/inginious/tasks";
+      example = "/var/lib/INGInious/tasks";
+      description = ''
+        Path to the tasks folder.
+        Defaults to the provided test tasks folder (readonly).
+      '';
+    };
+
+    useLTI = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''Whether to start the LTI frontend in place of the webapp.'';
+    };
+
+    superadmins = mkOption {
+      type = types.uniq (types.listOf types.str);
+      default = [ "admin" ];
+      example = [ "john" "pepe" "emilia" ];
+      description = ''List of user logins allowed to administrate the whole server.'';
+    };
+
+    containers = mkOption {
+      type = types.attrsOf types.str;
+      default = {
+          default = "ingi/inginious-c-default";
+      };
+      example = {
+        default = "ingi/inginious-c-default";
+        sekexe  = "ingi/inginious-c-sekexe";
+        java    = "ingi/inginious-c-java";
+        oz      = "ingi/inginious-c-oz";
+        pythia1compat = "ingi/inginious-c-pythia1compat";
+      };
+      description = ''
+        An attrset describing the required containers
+        These containers will be available in INGInious using their short name (key)
+        and will be automatically downloaded before INGInious starts.
+      '';
+    };
+
+    hostPattern = mkOption {
+      type = types.str;
+      default = "^inginious.";
+      example = "^inginious.mydomain.xyz$";
+      description = ''
+        The domain that serves INGInious.
+        INGInious uses absolute paths which makes it difficult to relocate in its own subdir.
+        The default configuration will serve INGInious when the server is accessed with a hostname starting with "inginious.".
+        If left blank, INGInious will take the precedence over all the other lighttpd sites, which is probably not what you want.
+      '';
+    };
+
+    backendType = mkOption {
+      type = types.enum [ "local" "remote_manual" ]; # TODO: support backend "remote"
+      default = "local";
+      description = ''
+        Select how INGINious accesses to grading containers.
+        The default "local" option ensures that Docker is started and provisioned.
+        Fore more information, see http://inginious.readthedocs.io/en/latest/install_doc/config_reference.html
+        Not all backends are supported. Use services.inginious.configFile for full flexibility.
+      '';
+    };
+
+    remoteAgents = mkOption {
+      type = types.listOf (types.attrsOf types.str);
+      default = [];
+      example = [ { host = "192.0.2.25"; port = "1345"; } ];
+      description = ''A list of remote agents, used only when services.inginious.backendType is "remote_manual".'';
+    };
+  };
+
+  config = mkIf cfg.enable (
+    mkMerge [
+      # For a local install, we need docker.
+      (mkIf (cfg.backendType == "local") {
+        virtualisation.docker = {
+          enable = true;
+          # We need docker to listen on port 2375.
+          extraOptions = "-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock";
+          storageDriver = mkDefault "overlay";
+          socketActivation = false;
+        };
+
+        users.extraUsers."lighttpd".extraGroups = [ "docker" ];
+
+        # Ensure that docker has pulled the required images.
+        systemd.services.inginious-prefetch = {
+          script = let
+            images = lib.unique (
+              [ "centos" "ingi/inginious-agent" ]
+              ++ lib.mapAttrsToList (_: image: image) cfg.containers
+            );
+          in lib.concatMapStrings (image: ''
+            ${pkgs.docker}/bin/docker pull ${image}
+          '') images;
+
+          serviceConfig.Type = "oneshot";
+          wants = [ "docker.service" ];
+          after = [ "docker.service" ];
+          wantedBy = [ "lighttpd.service" ];
+          before = [ "lighttpd.service" ];
+        };
+      })
+
+      # Common
+      {
+        # To access inginous tools (like inginious-test-task)
+        environment.systemPackages = [ inginious ];
+
+        services.mongodb.enable = true;
+
+        services.lighttpd.enable = true;
+        services.lighttpd.enableModules = [ "mod_access" "mod_alias" "mod_fastcgi" "mod_redirect" "mod_rewrite" ];
+        services.lighttpd.extraConfig = ''
+          $HTTP["host"] =~ "${cfg.hostPattern}" {
+            fastcgi.server = ( "/${execName}" =>
+              ((
+                "socket" => "/run/lighttpd/inginious-fastcgi.socket",
+                "bin-path" => "${inginious}/bin/${execName} --config=${inginiousConfigFile}",
+                "max-procs" => 1,
+                "bin-environment" => ( "REAL_SCRIPT_NAME" => "" ),
+                "check-local" => "disable"
+              ))
+            )
+            url.rewrite-once = (
+              "^/.well-known/.*" => "$0",
+              "^/static/.*" => "$0",
+              "^/.*$" => "/${execName}$0",
+              "^/favicon.ico$" => "/static/common/favicon.ico",
+            )
+            alias.url += (
+              "/static/webapp/" => "${inginious}/lib/python2.7/site-packages/inginious/frontend/webapp/static/",
+              "/static/common/" => "${inginious}/lib/python2.7/site-packages/inginious/frontend/common/static/"
+            )
+          }
+        '';
+
+        systemd.services.lighttpd.preStart = ''
+          mkdir -p /run/lighttpd
+          chown lighttpd.lighttpd /run/lighttpd
+        '';
+
+        systemd.services.lighttpd.wants = [ "mongodb.service" "docker.service" ];
+        systemd.services.lighttpd.after = [ "mongodb.service" "docker.service" ];
+      }
+    ]);
+}
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 91601f387cbe..89b515a6e275 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -78,6 +78,8 @@ in {
         type = types.listOf types.path;
         description = "List of packages for which gsettings are overridden.";
       };
+
+      debug = mkEnableOption "gnome-session debug messages";
     };  
 
     environment.gnome3.packageSet = mkOption {
@@ -117,7 +119,10 @@ in {
     services.telepathy.enable = mkDefault true;
     networking.networkmanager.enable = mkDefault true;
     services.upower.enable = config.powerManagement.enable;
+    services.dbus.packages = mkIf config.services.printing.enable [ pkgs.system-config-printer ];
+    services.colord.enable = mkDefault true;
     hardware.bluetooth.enable = mkDefault true;
+    services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
 
     fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell_fonts ];
 
@@ -159,7 +164,7 @@ in {
           # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
           ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
 
-          ${gnome3.gnome_session}/bin/gnome-session&
+          ${gnome3.gnome_session}/bin/gnome-session ${optionalString cfg.debug "--debug"} &
           waitPID=$!
         '';
       };
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 4c4e3d967988..634d2a39576a 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -33,6 +33,14 @@ in
         default = false;
         description = "Don't install XFCE desktop components (xfdesktop, panel and notification daemon).";
       };
+
+      extraSessionCommands = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Shell commands executed just before XFCE is started.
+        '';
+      };
     };
 
   };
@@ -45,6 +53,8 @@ in
         bgSupport = true;
         start =
           ''
+            ${cfg.extraSessionCommands}
+
             # Set GTK_PATH so that GTK+ can find the theme engines.
             export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
 
diff --git a/nixos/modules/services/x11/window-managers/i3.nix b/nixos/modules/services/x11/window-managers/i3.nix
index 2e2e10cc33b0..cfe9439b688c 100644
--- a/nixos/modules/services/x11/window-managers/i3.nix
+++ b/nixos/modules/services/x11/window-managers/i3.nix
@@ -3,37 +3,52 @@
 with lib;
 
 let
-  cfg = config.services.xserver.windowManager.i3;
-in
+  wmCfg = config.services.xserver.windowManager;
 
-{
-  options = {
-    services.xserver.windowManager.i3 = {
-      enable = mkEnableOption "i3";
-
-      configFile = mkOption {
-        default = null;
-        type = types.nullOr types.path;
-        description = ''
-          Path to the i3 configuration file.
-          If left at the default value, $HOME/.i3/config will be used.
-        '';
-      };
+  i3option = name: {
+    enable = mkEnableOption name;
+    configFile = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = ''
+        Path to the i3 configuration file.
+        If left at the default value, $HOME/.i3/config will be used.
+      '';
+    };
+    extraSessionCommands = mkOption {
+      default = "";
+      type = types.lines;
+      description = ''
+        Shell commands executed just before i3 is started.
+      '';
     };
   };
 
-  config = mkIf cfg.enable {
-    services.xserver.windowManager = {
-      session = [{
-        name = "i3";
-        start = ''
-          ${pkgs.i3}/bin/i3 ${optionalString (cfg.configFile != null)
-            "-c \"${cfg.configFile}\""
-          } &
-          waitPID=$!
-        '';
-      }];
-    };
-    environment.systemPackages = with pkgs; [ i3 ];
+  i3config = name: pkg: cfg: {
+    services.xserver.windowManager.session = [{
+      inherit name;
+      start = ''
+        ${cfg.extraSessionCommands}
+
+        ${pkg}/bin/i3 ${optionalString (cfg.configFile != null)
+          "-c \"${cfg.configFile}\""
+        } &
+        waitPID=$!
+      '';
+    }];
+    environment.systemPackages = [ pkg ];
   };
+
+in
+
+{
+  options.services.xserver.windowManager = {
+    i3 = i3option "i3";
+    i3-gaps = i3option "i3-gaps";
+  };
+
+  config = mkMerge [
+    (mkIf wmCfg.i3.enable (i3config "i3" pkgs.i3 wmCfg.i3))
+    (mkIf wmCfg.i3-gaps.enable (i3config "i3-gaps" pkgs.i3-gaps wmCfg.i3-gaps))
+  ];
 }
diff --git a/nixos/modules/services/x11/xbanish.nix b/nixos/modules/services/x11/xbanish.nix
new file mode 100644
index 000000000000..e1e3cbc8e441
--- /dev/null
+++ b/nixos/modules/services/x11/xbanish.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.xbanish;
+
+in {
+  options.services.xbanish = {
+
+    enable = mkEnableOption "xbanish";
+
+    arguments = mkOption {
+      description = "Arguments to pass to xbanish command";
+      default = "";
+      example = "-d -i shift";
+      type = types.str;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user.services.xbanish = {
+      description = "xbanish hides the mouse pointer";
+      wantedBy = [ "default.target" ];
+      serviceConfig.ExecStart = ''
+        ${pkgs.xbanish}/bin/xbanish ${cfg.arguments}
+      '';
+      serviceConfig.Restart = "always";
+    };
+  };
+}
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 093b7f1ff225..bb97d0c53a6b 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -261,7 +261,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
 
 sub pathToUnitName {
     my ($path) = @_;
-    open my $cmd, "-|", "systemd-escape", "--suffix=mount", "-p", $path
+    open my $cmd, "-|", "@systemd@/bin/systemd-escape", "--suffix=mount", "-p", $path
         or die "Unable to escape $path!\n";
     my $escaped = join "", <$cmd>;
     chomp $escaped;
diff --git a/nixos/modules/system/boot/coredump.nix b/nixos/modules/system/boot/coredump.nix
index 793c7515c761..b27a35b6257d 100644
--- a/nixos/modules/system/boot/coredump.nix
+++ b/nixos/modules/system/boot/coredump.nix
@@ -36,6 +36,8 @@ with lib;
   config = mkMerge [
     (mkIf config.systemd.coredump.enable {
 
+      systemd.additionalUpstreamSystemUnits = [ "systemd-coredump.socket" "systemd-coredump@.service" ];
+
       environment.etc."systemd/coredump.conf".text =
         ''
           [Coredump]
@@ -45,7 +47,7 @@ with lib;
       # Have the kernel pass core dumps to systemd's coredump helper binary.
       # From systemd's 50-coredump.conf file. See:
       # <https://github.com/systemd/systemd/blob/v218/sysctl.d/50-coredump.conf.in>
-      boot.kernel.sysctl."kernel.core_pattern" = "|${pkgs.systemd}/lib/systemd/systemd-coredump %p %u %g %s %t %e";
+      boot.kernel.sysctl."kernel.core_pattern" = "|${pkgs.systemd}/lib/systemd/systemd-coredump %P %u %g %s %t %c %e";
     })
 
     (mkIf (!config.systemd.coredump.enable) {
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index 3e2805a8c341..d0a4ce51148f 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -100,9 +100,6 @@ in
     '';
 
     boot.initrd.network.postCommands = ''
-      mkdir /dev/pts
-      mount -t devpts devpts /dev/pts
-
       echo '${cfg.shell}' > /etc/shells
       echo 'root:x:0:0:root:/root:${cfg.shell}' > /etc/passwd
       echo 'passwd: files' > /etc/nsswitch.conf
diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
index c5c250c14cea..576a07c1d272 100644
--- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
+++ b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
@@ -5,5 +5,4 @@ pkgs.substituteAll {
   isExecutable = true;
   path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
   inherit (pkgs) bash;
-  kernelDTB = pkgs.stdenv.platform.kernelDTB or false;
 }
diff --git a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
index 78a8e8fd658c..c780a89b102c 100644
--- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
+++ b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
@@ -75,9 +75,10 @@ addEntry() {
 
     copyToKernelsDir "$path/kernel"; kernel=$result
     copyToKernelsDir "$path/initrd"; initrd=$result
-    if [ -n "@kernelDTB@" ]; then
-        # XXX UGLY: maybe the system config should have a top-level "dtbs" entry?
-        copyToKernelsDir $(readlink -m "$path/kernel/../dtbs"); dtbs=$result
+    # XXX UGLY: maybe the system config should have a top-level "dtbs" entry?
+    dtbDir=$(readlink -m "$path/kernel/../dtbs")
+    if [ -d "$dtbDir" ]; then
+        copyToKernelsDir "$dtbDir"; dtbs=$result
     fi
 
     timestampEpoch=$(stat -L -c '%Z' $path)
@@ -95,7 +96,7 @@ addEntry() {
     fi
     echo "  LINUX ../nixos/$(basename $kernel)"
     echo "  INITRD ../nixos/$(basename $initrd)"
-    if [ -n "@kernelDTB@" ]; then
+    if [ -d "$dtbDir" ]; then
         echo "  FDTDIR ../nixos/$(basename $dtbs)"
     fi
     echo "  APPEND systemConfig=$path init=$path/init $extraParams"
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index b8ef02da4bc2..94d87b436065 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -501,7 +501,7 @@ sub getEfiTarget {
 my @deviceTargets = getDeviceTargets();
 my $efiTarget = getEfiTarget();
 my $prevGrubState = readGrubState();
-my @prevDeviceTargets = split/:/, $prevGrubState->devices;
+my @prevDeviceTargets = split/,/, $prevGrubState->devices;
 
 my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference());
 my $nameDiffer = get("fullName") ne $prevGrubState->name;
@@ -549,7 +549,7 @@ if ($requireNewInstall != 0) {
     print FILE get("fullName"), "\n" or die;
     print FILE get("fullVersion"), "\n" or die;
     print FILE $efiTarget, "\n" or die;
-    print FILE join( ":", @deviceTargets ), "\n" or die;
+    print FILE join( ",", @deviceTargets ), "\n" or die;
     print FILE $efiSysMountPoint, "\n" or die;
     close FILE or die;
 }
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 8dad09c89207..15881b6d3714 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -36,7 +36,7 @@ let
           ${optionalString (header != null) "--header=${header}"} \
           ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"} \
           > /.luksopen_args
-        cryptsetup-askpass
+        get_password "Enter LUKS Passphrase" cryptsetup-askpass
         rm /.luksopen_args
     }
 
@@ -78,9 +78,7 @@ let
         for try in $(seq 3); do
 
             ${optionalString yubikey.twoFactor ''
-            echo -n "Enter two-factor passphrase: "
-            read -s k_user
-            echo
+            k_user="$(get_password "Enter two-factor passphrase" cat)"
             ''}
 
             if [ ! -z "$k_user" ]; then
@@ -463,6 +461,26 @@ in
       ''}
     '';
 
+    boot.initrd.preDeviceCommands = ''
+      get_password() {
+        local ret
+        local reply
+        local tty_stat
+
+        tty_stat="$(stty -g)"
+        stty -echo
+        for i in `seq 1 3`; do
+          echo -n "$1: "
+          read reply
+          echo "$reply" | "$2"
+          if [ "$?" = "0" ]; then
+            break
+          fi
+        done
+        stty "$tty_stat"
+      }
+    '';
+
     boot.initrd.preLVMCommands = concatStrings (mapAttrsToList openCommand preLVM);
     boot.initrd.postDeviceCommands = concatStrings (mapAttrsToList openCommand postLVM);
 
diff --git a/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix
new file mode 100644
index 000000000000..82c1032937c6
--- /dev/null
+++ b/nixos/modules/system/boot/plymouth.nix
@@ -0,0 +1,129 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  inherit (pkgs) plymouth;
+
+  cfg = config.boot.plymouth;
+
+  themesEnv = pkgs.buildEnv {
+    name = "plymouth-themes";
+    paths = [ plymouth ] ++ cfg.themePackages;
+  };
+
+  configFile = pkgs.writeText "plymouthd.conf" ''
+    [Daemon]
+    ShowDelay=0
+    Theme=${cfg.theme}
+  '';
+
+in
+
+{
+
+  options = {
+
+    boot.plymouth = {
+
+      enable = mkEnableOption "Plymouth boot splash screen";
+
+      themePackages = mkOption {
+        default = [];
+        type = types.listOf types.package;
+        description = ''
+          Extra theme packages for plymouth.
+        '';
+      };
+
+      theme = mkOption {
+        default = "fade-in";
+        type = types.str;
+        description = ''
+          Splash screen theme.
+        '';
+      };
+
+      logo = mkOption {
+        type = types.path;
+        default = pkgs.fetchurl {
+          url = "https://nixos.org/logo/nixos-hires.png";
+          sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
+        };
+        description = ''
+          Logo which is displayed on the splash screen.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    boot.kernelParams = [ "splash" ];
+
+    # To be discoverable by systemd.
+    environment.systemPackages = [ plymouth ];
+
+    environment.etc."plymouth/plymouthd.conf".source = configFile;
+    environment.etc."plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouth.defaults";
+    environment.etc."plymouth/logo.png".source = cfg.logo;
+    environment.etc."plymouth/themes".source = "${themesEnv}/share/plymouth/themes";
+    # XXX: Needed because we supply a different set of plugins in initrd.
+    environment.etc."plymouth/plugins".source = "${plymouth}/lib/plymouth";
+
+    systemd.packages = [ plymouth ];
+
+    systemd.services.plymouth-kexec.wantedBy = [ "kexec.target" ];
+    systemd.services.plymouth-halt.wantedBy = [ "halt.target" ];
+    systemd.services.plymouth-quit = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "display-manager.service" "multi-user.target" ];
+    };
+    systemd.services.plymouth-poweroff.wantedBy = [ "poweroff.target" ];
+    systemd.services.plymouth-reboot.wantedBy = [ "reboot.target" ];
+    systemd.services.plymouth-read-write.wantedBy = [ "sysinit.target" ];
+
+    boot.initrd.extraUtilsCommands = ''
+      copy_bin_and_libs ${pkgs.plymouth}/bin/plymouthd
+      copy_bin_and_libs ${pkgs.plymouth}/bin/plymouth
+
+      moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)"
+
+      mkdir -p $out/lib/plymouth/renderers
+      cp ${plymouth}/lib/plymouth/{text,details,$moduleName}.so $out/lib/plymouth
+      cp ${plymouth}/lib/plymouth/renderers/{drm,frame-buffer}.so $out/lib/plymouth/renderers
+
+      mkdir -p $out/share/plymouth/themes
+      cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth
+      cp -r ${themesEnv}/share/plymouth/themes/{text,details,${cfg.theme}} $out/share/plymouth/themes
+      cp ${cfg.logo} $out/share/plymouth/logo.png
+    '';
+
+    boot.initrd.extraUtilsCommandsTest = ''
+      $out/bin/plymouthd --help >/dev/null
+      $out/bin/plymouth --help >/dev/null
+    '';
+
+    boot.initrd.extraUdevRulesCommands = ''
+      cp ${config.systemd.package}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out
+      sed -i '/loginctl/d' $out/71-seat.rules
+    '';
+
+    boot.initrd.preLVMCommands = mkAfter ''
+      mkdir -p /etc/plymouth
+      ln -s ${configFile} /etc/plymouth/plymouthd.conf
+      ln -s $extraUtils/share/plymouth/plymouthd.defaults /etc/plymouth/plymouthd.defaults
+      ln -s $extraUtils/share/plymouth/logo.png /etc/plymouth/logo.png
+      ln -s $extraUtils/share/plymouth/themes /etc/plymouth/themes
+      ln -s $extraUtils/lib/plymouth /etc/plymouth/plugins
+
+      plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session
+      plymouth --show-splash
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix
index 5a98b9b6d480..4b7c545dcc0d 100644
--- a/nixos/modules/system/boot/resolved.nix
+++ b/nixos/modules/system/boot/resolved.nix
@@ -1,7 +1,9 @@
 { config, lib, pkgs, ... }:
 
 with lib;
-
+let
+  cfg = config.services.resolved;
+in
 {
 
   options = {
@@ -14,9 +16,60 @@ with lib;
       '';
     };
 
+    services.resolved.fallbackDns = mkOption {
+      default = [ ];
+      example = [ "8.8.8.8" "2001:4860:4860::8844" ];
+      type = types.listOf types.str;
+      description = ''
+        A list of IPv4 and IPv6 addresses to use as the fallback DNS servers.
+        If this option is empty, a compiled-in list of DNS servers is used instead.
+      '';
+    };
+
+    services.resolved.domains = mkOption {
+      default = config.networking.search;
+      example = [ "example.com" ];
+      type = types.listOf types.str;
+      description = ''
+        A list of domains. These domains are used as search suffixes when resolving single-label host names (domain names which contain no dot), in order to qualify them into fully-qualified domain names (FQDNs).
+        For compatibility reasons, if this setting is not specified, the search domains listed in /etc/resolv.conf are used instead, if that file exists and any domains are configured in it.
+      '';
+    };
+
+    services.resolved.llmnr = mkOption {
+      default = "true";
+      example = "false";
+      type = types.enum [ "true" "resolve" "false" ];
+      description = ''
+        Controls Link-Local Multicast Name Resolution support (RFC 4794) on the local host.
+        If true, enables full LLMNR responder and resolver support.
+        If false, disables both.
+        If set to "resolve", only resolution support is enabled, but responding is disabled.
+      '';
+    };
+
+    services.resolved.dnssec = mkOption {
+      default = "allow-downgrade";
+      example = "true";
+      type = types.enum [ "true" "allow-downgrade" "false" ];
+      description = ''
+        If true all DNS lookups are DNSSEC-validated locally (excluding LLMNR and Multicast DNS). Note that this mode requires a DNS server that supports DNSSEC. If the DNS server does not properly support DNSSEC all validations will fail.
+        If set to "allow-downgrade" DNSSEC validation is attempted, but if the server does not support DNSSEC properly, DNSSEC mode is automatically disabled. Note that this mode makes DNSSEC validation vulnerable to "downgrade" attacks, where an attacker might be able to trigger a downgrade to non-DNSSEC mode by synthesizing a DNS response that suggests DNSSEC was not supported.
+        If set to false, DNS lookups are not DNSSEC validated.
+      '';
+    };
+
+    services.resolved.extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      description = ''
+        Extra config to append to resolved.conf.
+      '';
+    };
+
   };
 
-  config = mkIf config.services.resolved.enable {
+  config = mkIf cfg.enable {
 
     systemd.additionalUpstreamSystemUnits = [ "systemd-resolved.service" ];
 
@@ -27,7 +80,15 @@ with lib;
 
     environment.etc."systemd/resolved.conf".text = ''
       [Resolve]
-      DNS=${concatStringsSep " " config.networking.nameservers}
+      ${optionalString (config.networking.nameservers != [])
+        "DNS=${concatStringsSep " " config.networking.nameservers}"}
+      ${optionalString (cfg.fallbackDns != [])
+        "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
+      ${optionalString (cfg.domains != [])
+        "Domains=${concatStringsSep " " cfg.domains}"}
+      LLMNR=${cfg.llmnr}
+      DNSSEC=${cfg.dnssec}
+      ${config.services.resolved.extraConfig}
     '';
 
   };
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 9bffcd31b9b4..6b1bf0b3e028 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -3,6 +3,7 @@
 targetRoot=/mnt-root
 console=tty1
 
+extraUtils="@extraUtils@"
 export LD_LIBRARY_PATH=@extraUtils@/lib
 export PATH=@extraUtils@/bin
 ln -s @extraUtils@/bin /bin
@@ -13,6 +14,9 @@ export LVM_SUPPRESS_FD_WARNINGS=true
 fail() {
     if [ -n "$panicOnFail" ]; then exit 1; fi
 
+    # If we have a splash screen started, quit it.
+    command -v plymouth >/dev/null 2>&1 && plymouth quit
+
     # If starting stage 2 failed, allow the user to repair the problem
     # in an interactive shell.
     cat <<EOF
@@ -70,6 +74,8 @@ mount -t sysfs sysfs /sys
 mount -t devtmpfs -o "size=@devSize@" devtmpfs /dev
 mkdir -p /run
 mount -t tmpfs -o "mode=0755,size=@runSize@" tmpfs /run
+mkdir /dev/pts
+mount -t devpts devpts /dev/pts
 
 # Log the script output to /dev/kmsg or /run/log/stage-1-init.log.
 mkdir -p /tmp
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 5e6554324ca4..56a9c38b8f2b 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -81,9 +81,9 @@ let
       # Copy ld manually since it isn't detected correctly
       cp -pv ${pkgs.glibc.out}/lib/ld*.so.? $out/lib
 
-      # Copy all of the needed libraries for the binaries
-      for BIN in $(find $out/{bin,sbin} -type f); do
-        echo "Copying libs for bin $BIN"
+      # Copy all of the needed libraries
+      find $out/bin $out/lib -type f | while read BIN; do
+        echo "Copying libs for executable $BIN"
         LDD="$(ldd $BIN)" || continue
         LIBS="$(echo "$LDD" | awk '{print $3}' | sed '/^$/d')"
         for LIB in $LIBS; do
@@ -104,13 +104,17 @@ let
       stripDirs "lib bin" "-s"
 
       # Run patchelf to make the programs refer to the copied libraries.
-      for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs -e $out $i; fi; done
+      find $out/bin $out/lib -type f | while read i; do
+        if ! test -L $i; then
+          nuke-refs -e $out $i
+        fi
+      done
 
-      for i in $out/bin/*; do
-          if ! test -L $i; then
-              echo "patching $i..."
-              patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
-          fi
+      find $out/bin -type f | while read i; do
+        if ! test -L $i; then
+          echo "patching $i..."
+          patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
+        fi
       done
 
       # Make sure that the patchelf'ed binaries still work.
@@ -138,6 +142,7 @@ let
 
   udevRules = pkgs.stdenv.mkDerivation {
     name = "udev-rules";
+    allowedReferences = [ extraUtils ];
     buildCommand = ''
       mkdir -p $out
 
@@ -160,7 +165,8 @@ let
             --replace /sbin/mdadm ${extraUtils}/bin/mdadm \
             --replace /bin/sh ${extraUtils}/bin/sh \
             --replace /usr/bin/readlink ${extraUtils}/bin/readlink \
-            --replace /usr/bin/basename ${extraUtils}/bin/basename
+            --replace /usr/bin/basename ${extraUtils}/bin/basename \
+            --replace ${udev}/bin/udevadm ${extraUtils}/bin/udevadm
       done
 
       # Work around a bug in QEMU, which doesn't implement the "READ
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 076bbca850d9..5c8cce5066af 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -669,6 +669,7 @@ in
 
       "systemd/logind.conf".text = ''
         [Login]
+        KillUserProcesses=no
         ${config.services.logind.extraConfig}
       '';
 
@@ -763,7 +764,7 @@ in
         { wantedBy = [ "timers.target" ];
           timerConfig.OnCalendar = service.startAt;
         })
-        (filterAttrs (name: service: service.startAt != "") cfg.services);
+        (filterAttrs (name: service: service.enable && service.startAt != "") cfg.services);
 
     # Generate timer units for all services that have a ‘startAt’ value.
     systemd.user.timers =
@@ -793,6 +794,8 @@ in
     systemd.services.systemd-remount-fs.restartIfChanged = false;
     systemd.services.systemd-update-utmp.restartIfChanged = false;
     systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
+    systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
+    systemd.services.systemd-logind.stopIfChanged = false;
     systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.automount" ];
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index dd351306cb63..cf8232c36154 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -111,15 +111,17 @@ in
 
     fileSystems = mkOption {
       default = {};
-      example = {
-        "/".device = "/dev/hda1";
-        "/data" = {
-          device = "/dev/hda2";
-          fsType = "ext3";
-          options = [ "data=journal" ];
-        };
-        "/bigdisk".label = "bigdisk";
-      };
+      example = literalExample ''
+        {
+          "/".device = "/dev/hda1";
+          "/data" = {
+            device = "/dev/hda2";
+            fsType = "ext3";
+            options = [ "data=journal" ];
+          };
+          "/bigdisk".label = "bigdisk";
+        }
+      '';
       type = types.loaOf types.optionSet;
       options = [ fileSystemOpts ];
       description = ''
diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix
index 02721bb3bea2..8cdd34ef174c 100644
--- a/nixos/modules/tasks/kbd.nix
+++ b/nixos/modules/tasks/kbd.nix
@@ -5,14 +5,33 @@ with lib;
 let
 
   makeColor = n: value: "COLOR_${toString n}=${value}";
+  makeColorCS =
+    let positions = [ "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F" ];
+    in n: value: "\033]P${elemAt position n}${value}";
   colors = concatImapStringsSep "\n" makeColor config.i18n.consoleColors;
 
+  isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale);
+
+  optimizedKeymap = pkgs.runCommand "keymap" {
+    nativeBuildInputs = [ pkgs.kbd ];
+  } ''
+    cd ${kbdEnv}/share/keymaps
+    loadkeys -b ${optionalString isUnicode "-u"} "${config.i18n.consoleKeyMap}" > $out
+  '';
+
+  # Sadly, systemd-vconsole-setup doesn't support binary keymaps.
   vconsoleConf = pkgs.writeText "vconsole.conf" ''
     KEYMAP=${config.i18n.consoleKeyMap}
     FONT=${config.i18n.consoleFont}
     ${colors}
   '';
 
+  kbdEnv = pkgs.buildEnv {
+    name = "kbd-env";
+    paths = [ pkgs.kbd ] ++ config.i18n.consolePackages;
+    pathsToLink = [ "/share/consolefonts" "/share/consoletrans" "/share/keymaps" "/share/unimaps" ];
+  };
+
   setVconsole = !config.boot.isContainer;
 in
 
@@ -38,38 +57,75 @@ in
       '';
     };
 
+    boot.earlyVconsoleSetup = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Enable setting font and keymap as early as possible (in initrd).
+      '';
+    };
+
   };
 
 
   ###### implementation
 
   config = mkMerge [
-    (mkIf (!setVconsole) {
+    (mkIf (!setVconsole || (setVconsole && config.boot.earlyVconsoleSetup)) {
       systemd.services."systemd-vconsole-setup".enable = false;
     })
 
-    (mkIf setVconsole {
-      environment.systemPackages = [ pkgs.kbd ];
-
-      # Let systemd-vconsole-setup.service do the work of setting up the
-      # virtual consoles.  FIXME: trigger a restart of
-      # systemd-vconsole-setup.service if /etc/vconsole.conf changes.
-      environment.etc = [ {
-        target = "vconsole.conf";
-        source = vconsoleConf;
-      } ];
-
-      # This is identical to the systemd-vconsole-setup.service unit
-      # shipped with systemd, except that it uses /dev/tty1 instead of
-      # /dev/tty0 to prevent putting the X server in non-raw mode, and
-      # it has a restart trigger.
-      systemd.services."systemd-vconsole-setup" =
-        { wantedBy = [ "multi-user.target" ];
-          before = [ "display-manager.service" ];
-          after = [ "systemd-udev-settle.service" ];
-          restartTriggers = [ vconsoleConf ];
-        };
-    })
+    (mkIf setVconsole (mkMerge [
+      { environment.systemPackages = [ pkgs.kbd ];
+
+        # Let systemd-vconsole-setup.service do the work of setting up the
+        # virtual consoles.
+        environment.etc."vconsole.conf".source = vconsoleConf;
+        # Provide kbd with additional packages.
+        environment.etc."kbd".source = "${kbdEnv}/share";
+      }
+
+      (mkIf (!config.boot.earlyVconsoleSetup) {
+        # This is identical to the systemd-vconsole-setup.service unit
+        # shipped with systemd, except that it uses /dev/tty1 instead of
+        # /dev/tty0 to prevent putting the X server in non-raw mode, and
+        # it has a restart trigger.
+        systemd.services."systemd-vconsole-setup" =
+          { wantedBy = [ "sysinit.target" ];
+            before = [ "display-manager.service" ];
+            after = [ "systemd-udev-settle.service" ];
+            restartTriggers = [ vconsoleConf kbdEnv ];
+          };
+      })
+
+      (mkIf config.boot.earlyVconsoleSetup {
+        boot.initrd.extraUtilsCommands = ''
+          mkdir -p $out/share/consolefonts
+          ${if substring 0 1 config.i18n.consoleFont == "/" then ''
+            font="${config.i18n.consoleFont}"
+          '' else ''
+            font="$(echo ${kbdEnv}/share/consolefonts/${config.i18n.consoleFont}.*)"
+          ''}
+          if [[ $font == *.gz ]]; then
+            gzip -cd $font > $out/share/consolefonts/font.psf
+          else
+            cp -L $font $out/share/consolefonts/font.psf
+          fi
+        '';
+
+        boot.initrd.preLVMCommands = mkBefore ''
+          kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
+          printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
+          loadkmap < ${optimizedKeymap}
+
+          setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
+
+          ${concatImapStringsSep "\n" (n: color: ''
+            printf "${makeColorCS n color}" >> /dev/console
+          '') config.i18n.consoleColors}
+        '';
+      })
+    ]))
   ];
 
 }
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index 9e8417cde1df..ebf398fa266f 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -20,7 +20,12 @@ let cfg = config.ec2; in
       autoResize = true;
     };
 
+    boot.extraModulePackages =
+      [ config.boot.kernelPackages.ixgbevf
+        config.boot.kernelPackages.ena
+      ];
     boot.initrd.kernelModules = [ "xen-blkfront" "xen-netfront" ];
+    boot.initrd.availableKernelModules = [ "ixgbevf" "ena" ];
     boot.kernelParams = mkIf cfg.hvm [ "console=ttyS0" ];
 
     # Prevent the nouveau kernel module from being loaded, as it
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index 97b2927cf1bd..c99fc78d49e7 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -95,7 +95,7 @@ in
           LimitNPROC = 1048576;
         } // proxy_env;
 
-        path = [ pkgs.kmod ] ++ (optional (cfg.storageDriver == "zfs") pkgs.zfs);
+        path = [ config.system.sbin.modprobe ] ++ (optional (cfg.storageDriver == "zfs") pkgs.zfs);
         environment.MODULE_DIR = "/run/current-system/kernel-modules/lib/modules";
 
         postStart = if cfg.socketActivation then "" else cfg.postStart;
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index 5257aaf62025..bdf6ed4dcd29 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -90,40 +90,48 @@
   "15.09".us-west-2.pv-ebs = "ami-005fb160";
   "15.09".us-west-2.pv-s3 = "ami-cd55bbad";
 
-  "16.03".ap-northeast-1.hvm-ebs = "ami-b6edf5d8";
-  "16.03".ap-northeast-1.hvm-s3 = "ami-b1e3fbdf";
-  "16.03".ap-northeast-1.pv-ebs = "ami-6190880f";
-  "16.03".ap-northeast-1.pv-s3 = "ami-908d95fe";
-  "16.03".ap-southeast-1.hvm-ebs = "ami-35b16656";
-  "16.03".ap-southeast-1.hvm-s3 = "ami-41be6922";
-  "16.03".ap-southeast-1.pv-ebs = "ami-4cb96e2f";
-  "16.03".ap-southeast-1.pv-s3 = "ami-3bb96e58";
-  "16.03".ap-southeast-2.hvm-ebs = "ami-debc91bd";
-  "16.03".ap-southeast-2.hvm-s3 = "ami-55bc9136";
-  "16.03".ap-southeast-2.pv-ebs = "ami-b38ba6d0";
-  "16.03".ap-southeast-2.pv-s3 = "ami-9e8ba6fd";
-  "16.03".eu-central-1.hvm-ebs = "ami-7c967413";
-  "16.03".eu-central-1.hvm-s3 = "ami-b29072dd";
-  "16.03".eu-central-1.pv-ebs = "ami-7a947615";
-  "16.03".eu-central-1.pv-s3 = "ami-729b791d";
-  "16.03".eu-west-1.hvm-ebs = "ami-ff27a98c";
-  "16.03".eu-west-1.hvm-s3 = "ami-6c21af1f";
-  "16.03".eu-west-1.pv-ebs = "ami-a33cb2d0";
-  "16.03".eu-west-1.pv-s3 = "ami-ec38b69f";
-  "16.03".sa-east-1.hvm-ebs = "ami-5bef6637";
-  "16.03".sa-east-1.hvm-s3 = "ami-55f87139";
-  "16.03".sa-east-1.pv-ebs = "ami-76e56c1a";
-  "16.03".sa-east-1.pv-s3 = "ami-e1f8718d";
-  "16.03".us-east-1.hvm-ebs = "ami-4bfd1926";
-  "16.03".us-east-1.hvm-s3 = "ami-60c5210d";
-  "16.03".us-east-1.pv-ebs = "ami-c0c92dad";
-  "16.03".us-east-1.pv-s3 = "ami-f9d63294";
-  "16.03".us-west-1.hvm-ebs = "ami-13aad473";
-  "16.03".us-west-1.hvm-s3 = "ami-e1a8d681";
-  "16.03".us-west-1.pv-ebs = "ami-c0a6d8a0";
-  "16.03".us-west-1.pv-s3 = "ami-6aa9d70a";
-  "16.03".us-west-2.hvm-ebs = "ami-265dad46";
-  "16.03".us-west-2.hvm-s3 = "ami-cd40b0ad";
-  "16.03".us-west-2.pv-ebs = "ami-7b4aba1b";
-  "16.03".us-west-2.pv-s3 = "ami-0849b968";
+  "16.03".ap-northeast-1.hvm-ebs = "ami-40619d21";
+  "16.03".ap-northeast-1.hvm-s3 = "ami-ce629eaf";
+  "16.03".ap-northeast-1.pv-ebs = "ami-ef639f8e";
+  "16.03".ap-northeast-1.pv-s3 = "ami-a1609cc0";
+  "16.03".ap-northeast-2.hvm-ebs = "ami-deca00b0";
+  "16.03".ap-northeast-2.hvm-s3 = "ami-a3b77dcd";
+  "16.03".ap-northeast-2.pv-ebs = "ami-7bcb0115";
+  "16.03".ap-northeast-2.pv-s3 = "ami-a2b77dcc";
+  "16.03".ap-south-1.hvm-ebs = "ami-0dff9562";
+  "16.03".ap-south-1.hvm-s3 = "ami-13f69c7c";
+  "16.03".ap-south-1.pv-ebs = "ami-0ef39961";
+  "16.03".ap-south-1.pv-s3 = "ami-e0c8a28f";
+  "16.03".ap-southeast-1.hvm-ebs = "ami-5e964a3d";
+  "16.03".ap-southeast-1.hvm-s3 = "ami-4d964a2e";
+  "16.03".ap-southeast-1.pv-ebs = "ami-ec9b478f";
+  "16.03".ap-southeast-1.pv-s3 = "ami-999b47fa";
+  "16.03".ap-southeast-2.hvm-ebs = "ami-9f7359fc";
+  "16.03".ap-southeast-2.hvm-s3 = "ami-987359fb";
+  "16.03".ap-southeast-2.pv-ebs = "ami-a2705ac1";
+  "16.03".ap-southeast-2.pv-s3 = "ami-a3705ac0";
+  "16.03".eu-central-1.hvm-ebs = "ami-17a45178";
+  "16.03".eu-central-1.hvm-s3 = "ami-f9a55096";
+  "16.03".eu-central-1.pv-ebs = "ami-c8a550a7";
+  "16.03".eu-central-1.pv-s3 = "ami-6ea45101";
+  "16.03".eu-west-1.hvm-ebs = "ami-b5b3d5c6";
+  "16.03".eu-west-1.hvm-s3 = "ami-c986e0ba";
+  "16.03".eu-west-1.pv-ebs = "ami-b083e5c3";
+  "16.03".eu-west-1.pv-s3 = "ami-3c83e54f";
+  "16.03".sa-east-1.hvm-ebs = "ami-f6eb7f9a";
+  "16.03".sa-east-1.hvm-s3 = "ami-93e773ff";
+  "16.03".sa-east-1.pv-ebs = "ami-cbb82ca7";
+  "16.03".sa-east-1.pv-s3 = "ami-abb82cc7";
+  "16.03".us-east-1.hvm-ebs = "ami-c123a3d6";
+  "16.03".us-east-1.hvm-s3 = "ami-bc25a5ab";
+  "16.03".us-east-1.pv-ebs = "ami-bd25a5aa";
+  "16.03".us-east-1.pv-s3 = "ami-a325a5b4";
+  "16.03".us-west-1.hvm-ebs = "ami-748bcd14";
+  "16.03".us-west-1.hvm-s3 = "ami-a68dcbc6";
+  "16.03".us-west-1.pv-ebs = "ami-048acc64";
+  "16.03".us-west-1.pv-s3 = "ami-208dcb40";
+  "16.03".us-west-2.hvm-ebs = "ami-8263a0e2";
+  "16.03".us-west-2.hvm-s3 = "ami-925c9ff2";
+  "16.03".us-west-2.pv-ebs = "ami-5e61a23e";
+  "16.03".us-west-2.pv-s3 = "ami-734c8f13";
 }
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 67fbb8263b05..ea503a9526f8 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -1,5 +1,3 @@
-# Systemd services for libvirtd.
-
 { config, lib, pkgs, ... }:
 
 with lib;
@@ -16,71 +14,59 @@ let
     ${cfg.extraConfig}
   '';
 
-in
+in {
 
-{
   ###### interface
 
   options = {
 
-    virtualisation.libvirtd.enable =
-      mkOption {
-        type = types.bool;
-        default = false;
-        description =
-          ''
-            This option enables libvirtd, a daemon that manages
-            virtual machines. Users in the "libvirtd" group can interact with
-            the daemon (e.g. to start or stop VMs) using the
-            <command>virsh</command> command line tool, among others.
-          '';
-      };
-
-    virtualisation.libvirtd.enableKVM =
-      mkOption {
-        type = types.bool;
-        default = true;
-        description =
-          ''
-            This option enables support for QEMU/KVM in libvirtd.
-          '';
-      };
+    virtualisation.libvirtd.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        This option enables libvirtd, a daemon that manages
+        virtual machines. Users in the "libvirtd" group can interact with
+        the daemon (e.g. to start or stop VMs) using the
+        <command>virsh</command> command line tool, among others.
+      '';
+    };
 
-    virtualisation.libvirtd.extraConfig =
-      mkOption {
-        type = types.lines;
-        default = "";
-        description =
-          ''
-            Extra contents appended to the libvirtd configuration file,
-            libvirtd.conf.
-          '';
-      };
+    virtualisation.libvirtd.enableKVM = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        This option enables support for QEMU/KVM in libvirtd.
+      '';
+    };
 
-    virtualisation.libvirtd.extraOptions =
-      mkOption {
-        type = types.listOf types.str;
-        default = [ ];
-        example = [ "--verbose" ];
-        description =
-          ''
-            Extra command line arguments passed to libvirtd on startup.
-          '';
-      };
+    virtualisation.libvirtd.extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra contents appended to the libvirtd configuration file,
+        libvirtd.conf.
+      '';
+    };
 
-    virtualisation.libvirtd.onShutdown =
-      mkOption {
-        type = types.enum ["shutdown" "suspend" ];
-        default = "suspend";
-        description =
-          ''
-            When shutting down / restarting the host what method should
-            be used to gracefully halt the guests. Setting to "shutdown"
-            will cause an ACPI shutdown of each guest. "suspend" will
-            attempt to save the state of the guests ready to restore on boot.
-          '';
-      };
+    virtualisation.libvirtd.extraOptions = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--verbose" ];
+      description = ''
+        Extra command line arguments passed to libvirtd on startup.
+      '';
+    };
 
+    virtualisation.libvirtd.onShutdown = mkOption {
+      type = types.enum ["shutdown" "suspend" ];
+      default = "suspend";
+      description = ''
+        When shutting down / restarting the host what method should
+        be used to gracefully halt the guests. Setting to "shutdown"
+        will cause an ACPI shutdown of each guest. "suspend" will
+        attempt to save the state of the guests ready to restore on boot.
+      '';
+    };
 
   };
 
@@ -95,103 +81,87 @@ in
 
     boot.kernelModules = [ "tun" ];
 
-    systemd.services.libvirtd =
-      { description = "Libvirt Virtual Machine Management Daemon";
-
-        wantedBy = [ "multi-user.target" ];
-        after = [ "systemd-udev-settle.service" ]
-                ++ optional vswitch.enable "vswitchd.service";
-
-        path = [ 
-            pkgs.bridge-utils 
-            pkgs.dmidecode 
-            pkgs.dnsmasq
-            pkgs.ebtables
-          ] 
-          ++ optional cfg.enableKVM pkgs.qemu_kvm
-          ++ optional vswitch.enable vswitch.package;
-
-        preStart =
-          ''
-            mkdir -p /var/log/libvirt/qemu -m 755
-            rm -f /var/run/libvirtd.pid
-
-            mkdir -p /var/lib/libvirt
-            mkdir -p /var/lib/libvirt/dnsmasq
-
-            chmod 755 /var/lib/libvirt
-            chmod 755 /var/lib/libvirt/dnsmasq
-
-            # Copy default libvirt network config .xml files to /var/lib
-            # Files modified by the user will not be overwritten
-            for i in $(cd ${pkgs.libvirt}/var/lib && echo \
-                libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \
-                libvirt/nwfilter/*.xml );
-            do
-                mkdir -p /var/lib/$(dirname $i) -m 755
-                cp -npd ${pkgs.libvirt}/var/lib/$i /var/lib/$i
-            done
-
-            # libvirtd puts the full path of the emulator binary in the machine
-            # config file. But this path can unfortunately be garbage collected
-            # while still being used by the virtual machine. So update the
-            # emulator path on each startup to something valid (re-scan $PATH).
-            for file in /etc/libvirt/qemu/*.xml /etc/libvirt/lxc/*.xml; do
-                test -f "$file" || continue
-                # get (old) emulator path from config file
-                emulator=$(grep "^[[:space:]]*<emulator>" "$file" | sed 's,^[[:space:]]*<emulator>\(.*\)</emulator>.*,\1,')
-                # get a (definitely) working emulator path by re-scanning $PATH
-                new_emulator=$(PATH=${pkgs.libvirt}/libexec:$PATH command -v $(basename "$emulator"))
-                # write back
-                sed -i "s,^[[:space:]]*<emulator>.*,    <emulator>$new_emulator</emulator> <!-- WARNING: emulator dirname is auto-updated by the nixos libvirtd module -->," "$file"
-            done
-          ''; # */
-
-        serviceConfig.ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" --daemon ${concatStringsSep " " cfg.extraOptions}'';
-        serviceConfig.Type = "forking";
-        serviceConfig.KillMode = "process"; # when stopping, leave the VMs alone
-
-        # Wait until libvirtd is ready to accept requests.
-        postStart =
-          ''
-            for ((i = 0; i < 60; i++)); do
-                if ${pkgs.libvirt}/bin/virsh list > /dev/null; then exit 0; fi
-                sleep 1
-            done
-            exit 1 # !!! seems to be ignored
-          '';
-      };
+    users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd;
 
-    systemd.services."libvirt-guests" = {
-      description = "Libvirt Virtual Machines";
+    systemd.services.libvirtd = {
+      description = "Libvirt Virtual Machine Management Daemon";
 
       wantedBy = [ "multi-user.target" ];
-      wants = [ "libvirtd.service" ];
-      after = [ "libvirtd.service" ];
-
-      restartIfChanged = false;
-
-      path = with pkgs; [ gettext libvirt gawk ];
+      after = [ "systemd-udev-settle.service" ]
+              ++ optional vswitch.enable "vswitchd.service";
+
+      path = [
+          pkgs.bridge-utils
+          pkgs.dmidecode
+          pkgs.dnsmasq
+          pkgs.ebtables
+        ]
+        ++ optional cfg.enableKVM pkgs.qemu_kvm
+        ++ optional vswitch.enable vswitch.package;
 
       preStart = ''
-        mkdir -p /var/lock/subsys -m 755
-        ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests start || true
-      '';
-
-      postStop = ''
-        export PATH=${pkgs.gettext}/bin:$PATH
-        export ON_SHUTDOWN=${cfg.onShutdown}
-        ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests stop
-      '';
+        mkdir -p /var/log/libvirt/qemu -m 755
+        rm -f /var/run/libvirtd.pid
+
+        mkdir -p /var/lib/libvirt
+        mkdir -p /var/lib/libvirt/dnsmasq
+
+        chmod 755 /var/lib/libvirt
+        chmod 755 /var/lib/libvirt/dnsmasq
+
+        # Copy default libvirt network config .xml files to /var/lib
+        # Files modified by the user will not be overwritten
+        for i in $(cd ${pkgs.libvirt}/var/lib && echo \
+            libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \
+            libvirt/nwfilter/*.xml );
+        do
+            mkdir -p /var/lib/$(dirname $i) -m 755
+            cp -npd ${pkgs.libvirt}/var/lib/$i /var/lib/$i
+        done
+
+        # libvirtd puts the full path of the emulator binary in the machine
+        # config file. But this path can unfortunately be garbage collected
+        # while still being used by the virtual machine. So update the
+        # emulator path on each startup to something valid (re-scan $PATH).
+        for file in /etc/libvirt/qemu/*.xml /etc/libvirt/lxc/*.xml; do
+            test -f "$file" || continue
+            # get (old) emulator path from config file
+            emulator=$(grep "^[[:space:]]*<emulator>" "$file" | sed 's,^[[:space:]]*<emulator>\(.*\)</emulator>.*,\1,')
+            # get a (definitely) working emulator path by re-scanning $PATH
+            new_emulator=$(PATH=${pkgs.libvirt}/libexec:$PATH command -v $(basename "$emulator"))
+            # write back
+            sed -i "s,^[[:space:]]*<emulator>.*,    <emulator>$new_emulator</emulator> <!-- WARNING: emulator dirname is auto-updated by the nixos libvirtd module -->," "$file"
+        done
+      ''; # */
 
       serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
+        ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
+        Type = "notify";
+        KillMode = "process"; # when stopping, leave the VMs alone
+        Restart = "on-failure";
       };
     };
 
-    users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd;
+    systemd.sockets.virtlogd = {
+      description = "Virtual machine log manager socket";
+      wantedBy = [ "sockets.target" ];
+      listenStreams = [ "/run/libvirt/virtlogd-sock" ];
+    };
 
-  };
+    systemd.services.virtlogd = {
+      description = "Virtual machine log manager";
+      serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlogd virtlogd";
+    };
 
+    systemd.sockets.virtlockd = {
+      description = "Virtual machine lock manager socket";
+      wantedBy = [ "sockets.target" ];
+      listenStreams = [ "/run/libvirt/virtlockd-sock" ];
+    };
+
+    systemd.services.virtlockd = {
+      description = "Virtual machine lock manager";
+      serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlockd virtlockd";
+    };
+  };
 }
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
index 845f14352f3d..9d76b890872a 100644
--- a/nixos/modules/virtualisation/lxd.nix
+++ b/nixos/modules/virtualisation/lxd.nix
@@ -47,7 +47,7 @@ in
         # TODO(wkennington): Add lvm2 and thin-provisioning-tools
         path = with pkgs; [ acl rsync gnutar xz btrfs-progs ];
 
-        serviceConfig.ExecStart = "@${pkgs.lxd}/bin/lxd lxd --syslog --group lxd";
+        serviceConfig.ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --syslog --group lxd";
         serviceConfig.Type = "simple";
         serviceConfig.KillMode = "process"; # when stopping, leave the containers alone
       };
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index f275291c716c..9cc1ee762a83 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -68,6 +68,7 @@ in rec {
         (all nixos.tests.boot.uefiCdrom)
         (all nixos.tests.boot.uefiUsb)
         (all nixos.tests.boot-stage1)
+        (all nixos.tests.ecryptfs)
         (all nixos.tests.ipv6)
         (all nixos.tests.i3wm)
         (all nixos.tests.kde4)
diff --git a/nixos/release.nix b/nixos/release.nix
index c8547784bbcf..1800122fa2e5 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -220,6 +220,7 @@ in rec {
   tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; });
   tests.dockerRegistry = hydraJob (import tests/docker-registry.nix { system = "x86_64-linux"; });
   tests.dnscrypt-proxy = callTest tests/dnscrypt-proxy.nix { system = "x86_64-linux"; };
+  tests.ecryptfs = callTest tests/ecryptfs.nix {};
   tests.etcd = hydraJob (import tests/etcd.nix { system = "x86_64-linux"; });
   tests.ec2-nixops = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-nixops;
   tests.ec2-config = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-config;
diff --git a/nixos/tests/ecryptfs.nix b/nixos/tests/ecryptfs.nix
new file mode 100644
index 000000000000..eceb25a0deac
--- /dev/null
+++ b/nixos/tests/ecryptfs.nix
@@ -0,0 +1,81 @@
+import ./make-test.nix ({ pkgs, ... }:
+{
+  name = "ecryptfs";
+
+  machine = { config, pkgs, ... }: {
+    imports = [ ./common/user-account.nix ];
+    boot.kernelModules = [ "ecryptfs" ];
+    security.pam.enableEcryptfs = true;
+    environment.systemPackages = with pkgs; [ keyutils ];
+  };
+
+  testScript = ''
+    $machine->waitForUnit("default.target");
+
+    # Set alice up with a password and a home
+    $machine->succeed("(echo foobar; echo foobar) | passwd alice");
+    $machine->succeed("chown -R alice.users ~alice");
+
+    # Migrate alice's home
+    my $out = $machine->succeed("echo foobar | ecryptfs-migrate-home -u alice");
+    $machine->log("ecryptfs-migrate-home said: $out");
+
+    # Log alice in (ecryptfs passwhrase is wrapped during first login)
+    $machine->sleep(2); # urgh: wait for username prompt
+    $machine->sendChars("alice\n");
+    $machine->sleep(1);
+    $machine->sendChars("foobar\n");
+    $machine->sleep(2);
+    $machine->sendChars("logout\n");
+    $machine->sleep(2);
+
+    # Why do I need to do this??
+    $machine->succeed("su alice -c ecryptfs-umount-private");
+    $machine->sleep(1);
+    $machine->fail("mount | grep ecryptfs"); # check that encrypted home is not mounted
+
+    # Show contents of the user keyring
+    my $out = $machine->succeed("su - alice -c 'keyctl list \@u'");
+    $machine->log("keyctl unlink said: " . $out);
+
+    # Log alice again
+    $machine->sendChars("alice\n");
+    $machine->sleep(1);
+    $machine->sendChars("foobar\n");
+    $machine->sleep(2);
+
+    # Create some files in encrypted home
+    $machine->succeed("su alice -c 'touch ~alice/a'");
+    $machine->succeed("su alice -c 'echo c > ~alice/b'");
+
+    # Logout
+    $machine->sendChars("logout\n");
+    $machine->sleep(2);
+
+    # Why do I need to do this??
+    $machine->succeed("su alice -c ecryptfs-umount-private");
+    $machine->sleep(1);
+
+    # Check that the filesystem is not accessible
+    $machine->fail("mount | grep ecryptfs");
+    $machine->succeed("su alice -c 'test \! -f ~alice/a'");
+    $machine->succeed("su alice -c 'test \! -f ~alice/b'");
+
+    # Log alice once more
+    $machine->sendChars("alice\n");
+    $machine->sleep(1);
+    $machine->sendChars("foobar\n");
+    $machine->sleep(2);
+
+    # Check that the files are there
+    $machine->sleep(1);
+    $machine->succeed("su alice -c 'test -f ~alice/a'");
+    $machine->succeed("su alice -c 'test -f ~alice/b'");
+    $machine->succeed(qq%test "\$(cat ~alice/b)" = "c"%);
+
+    # Catch https://github.com/NixOS/nixpkgs/issues/16766
+    $machine->succeed("su alice -c 'ls -lh ~alice/'");
+
+    $machine->sendChars("logout\n");
+  '';
+})
diff --git a/nixos/tests/emacs-daemon.nix b/nixos/tests/emacs-daemon.nix
new file mode 100644
index 000000000000..a4d63bdb7e41
--- /dev/null
+++ b/nixos/tests/emacs-daemon.nix
@@ -0,0 +1,45 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "emacs-daemon";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ DamienCassou ];
+  };
+
+  enableOCR = true;
+
+  machine =
+    { config, pkgs, ... }:
+
+    { imports = [ ./common/x11.nix ];
+      services.emacs = {
+        enable = true;
+        defaultEditor = true;
+      };
+
+      # Important to get the systemd service running for root
+      environment.variables.XDG_RUNTIME_DIR = "/run/user/0";
+
+      environment.variables.TEST_SYSTEM_VARIABLE = "system variable";
+    };
+
+  testScript =
+    ''
+      $machine->waitForUnit("multi-user.target");
+
+      # checks that the EDITOR environment variable is set
+      $machine->succeed("test \$(basename \"\$EDITOR\") = emacseditor");
+
+      # waits for the emacs service to be ready
+      $machine->waitUntilSucceeds("systemctl --user status emacs.service | grep 'Active: active'");
+
+      # connects to the daemon
+      $machine->succeed("emacsclient --create-frame \$EDITOR &");
+
+      # checks that Emacs shows the edited filename
+      $machine->waitForText("emacseditor");
+
+      # makes sure environment variables are accessible from Emacs
+      $machine->succeed("emacsclient --eval '(getenv \"TEST_SYSTEM_VARIABLE\")'") =~ /system variable/ or die;
+
+      $machine->screenshot("emacsclient");
+    '';
+})
diff --git a/nixos/tests/gnome3_20-gdm.nix b/nixos/tests/gnome3_18-gdm.nix
index 8b1e9afedfb9..8b1e9afedfb9 100644
--- a/nixos/tests/gnome3_20-gdm.nix
+++ b/nixos/tests/gnome3_18-gdm.nix
diff --git a/nixos/tests/gnome3_20.nix b/nixos/tests/gnome3_18.nix
index 51c83a4e3129..2c88e6abe890 100644
--- a/nixos/tests/gnome3_20.nix
+++ b/nixos/tests/gnome3_18.nix
@@ -15,7 +15,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       services.xserver.displayManager.auto.user = "alice";
       services.xserver.desktopManager.gnome3.enable = true;
 
-      environment.gnome3.packageSet = pkgs.gnome3_20;
+      environment.gnome3.packageSet = pkgs.gnome3_18;
 
       virtualisation.memorySize = 512;
     };
diff --git a/nixos/tests/grsecurity.nix b/nixos/tests/grsecurity.nix
index 14f1aa9ff885..aadbfd8371ff 100644
--- a/nixos/tests/grsecurity.nix
+++ b/nixos/tests/grsecurity.nix
@@ -3,17 +3,39 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "grsecurity";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ copumpkin ];
+    maintainers = [ copumpkin joachifm ];
   };
 
   machine = { config, pkgs, ... }:
-    { boot.kernelPackages = pkgs.linuxPackages_grsec_testing_server; };
+    { security.grsecurity.enable = true;
+      boot.kernel.sysctl."kernel.grsecurity.deter_bruteforce" = 0;
+      security.apparmor.enable = true;
+    };
 
-  testScript =
-    ''
-      $machine->succeed("uname -a") =~ /grsec/;
-      # FIXME: this seems to hang the whole test. Unclear why, but let's fix it
-      # $machine->succeed("${pkgs.paxtest}/bin/paxtest blackhat");
-    '';
-})
+  testScript = ''
+    subtest "grsec-lock", sub {
+      $machine->succeed("systemctl is-active grsec-lock");
+      $machine->succeed("grep -Fq 1 /proc/sys/kernel/grsecurity/grsec_lock");
+      $machine->fail("echo -n 0 >/proc/sys/kernel/grsecurity/grsec_lock");
+    };
+
+    subtest "paxtest", sub {
+      # TODO: running paxtest blackhat hangs the vm
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/anonmap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execbss") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execdata") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execheap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execstack") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotanon") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotbss") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotdata") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotheap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotstack") =~ /Killed/ or die;
+    };
 
+    subtest "tcc", sub {
+      $machine->execute("echo -e '#include <stdio.h>\nint main(void) { puts(\"hello\"); return 0; }' >main.c");
+      $machine->succeed("${pkgs.tinycc.bin}/bin/tcc -run main.c");
+    };
+  '';
+})