summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/default.nix4
-rw-r--r--nixos/doc/manual/configuration/firewall.xml19
-rw-r--r--nixos/doc/manual/configuration/ipv4-config.xml2
-rw-r--r--nixos/doc/manual/configuration/ipv6-config.xml2
-rw-r--r--nixos/doc/manual/configuration/user-mgmt.xml10
-rw-r--r--nixos/doc/manual/configuration/x-windows.xml9
-rw-r--r--nixos/doc/manual/configuration/xfce.xml13
-rw-r--r--nixos/doc/manual/default.nix126
-rw-r--r--nixos/doc/manual/development/option-declarations.xml9
-rw-r--r--nixos/doc/manual/development/option-types.xml10
-rw-r--r--nixos/doc/manual/development/testing-installer.xml6
-rw-r--r--nixos/doc/manual/development/writing-nixos-tests.xml41
-rw-r--r--nixos/doc/manual/installation/changing-config.xml18
-rw-r--r--nixos/doc/manual/installation/installing-from-other-distro.xml309
-rw-r--r--nixos/doc/manual/installation/installing-uefi.xml48
-rw-r--r--nixos/doc/manual/installation/installing-usb.xml22
-rw-r--r--nixos/doc/manual/installation/installing-virtualbox-guest.xml10
-rw-r--r--nixos/doc/manual/installation/installing.xml147
-rw-r--r--nixos/doc/manual/installation/obtaining.xml9
-rw-r--r--nixos/doc/manual/man-nixos-build-vms.xml6
-rw-r--r--nixos/doc/manual/man-nixos-enter.xml119
-rw-r--r--nixos/doc/manual/man-nixos-install.xml6
-rw-r--r--nixos/doc/manual/man-pages.xml3
-rw-r--r--nixos/doc/manual/options-to-docbook.xsl9
-rw-r--r--nixos/doc/manual/release-notes/release-notes.xml1
-rw-r--r--nixos/doc/manual/release-notes/rl-1404.xml2
-rw-r--r--nixos/doc/manual/release-notes/rl-1803.xml464
-rw-r--r--nixos/doc/manual/release-notes/rl-1809.xml85
-rw-r--r--nixos/doc/manual/style.css267
-rw-r--r--nixos/lib/build-vms.nix6
-rw-r--r--nixos/lib/eval-config.nix4
-rw-r--r--nixos/lib/make-disk-image.nix133
-rw-r--r--nixos/lib/make-ext4-fs.nix17
-rw-r--r--nixos/lib/make-iso9660-image.nix9
-rw-r--r--nixos/lib/make-iso9660-image.sh7
-rw-r--r--nixos/lib/make-squashfs.nix15
-rw-r--r--nixos/lib/make-system-tarball.nix14
-rw-r--r--nixos/lib/make-system-tarball.sh5
-rw-r--r--nixos/lib/qemu-flags.nix11
-rw-r--r--nixos/lib/test-driver/Machine.pm31
-rw-r--r--nixos/lib/testing.nix35
-rw-r--r--nixos/lib/testing/jquery-ui.nix24
-rw-r--r--nixos/lib/testing/jquery.nix36
-rw-r--r--nixos/maintainers/option-usages.nix6
-rw-r--r--nixos/maintainers/scripts/ec2/amazon-image.nix2
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh2
-rwxr-xr-xnixos/maintainers/scripts/gce/create-gce.sh4
-rw-r--r--nixos/modules/config/i18n.nix2
-rw-r--r--nixos/modules/config/networking.nix4
-rw-r--r--nixos/modules/config/no-x-libs.nix16
-rw-r--r--nixos/modules/config/nsswitch.nix18
-rw-r--r--nixos/modules/config/power-management.nix3
-rw-r--r--nixos/modules/config/pulseaudio.nix13
-rw-r--r--nixos/modules/config/shells-environment.nix2
-rw-r--r--nixos/modules/config/system-path.nix1
-rw-r--r--nixos/modules/config/users-groups.nix55
-rw-r--r--nixos/modules/config/zram.nix2
-rw-r--r--nixos/modules/hardware/all-firmware.nix3
-rw-r--r--nixos/modules/hardware/digitalbitbox.nix30
-rw-r--r--nixos/modules/hardware/onlykey.nix33
-rw-r--r--nixos/modules/hardware/onlykey.udev4
-rw-r--r--nixos/modules/hardware/opengl.nix161
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix16
-rw-r--r--nixos/modules/hardware/video/nvidia.nix21
-rw-r--r--nixos/modules/installer/cd-dvd/channel.nix6
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-base.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix70
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-aarch64.nix13
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix11
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image.nix34
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball.nix6
-rw-r--r--nixos/modules/installer/netboot/netboot.nix21
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix7
-rw-r--r--nixos/modules/installer/tools/nixos-enter.sh60
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl7
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh199
-rw-r--r--nixos/modules/installer/tools/nixos-prepare-root.sh104
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh2
-rw-r--r--nixos/modules/installer/tools/nixos-version.sh4
-rw-r--r--nixos/modules/installer/tools/tools.nix36
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix2
-rw-r--r--nixos/modules/misc/documentation.nix77
-rw-r--r--nixos/modules/misc/ids.nix44
-rw-r--r--nixos/modules/misc/label.nix72
-rw-r--r--nixos/modules/misc/locate.nix17
-rw-r--r--nixos/modules/misc/nixpkgs.nix123
-rw-r--r--nixos/modules/misc/version.nix78
-rw-r--r--nixos/modules/module-list.nix82
-rw-r--r--nixos/modules/profiles/all-hardware.nix5
-rw-r--r--nixos/modules/profiles/base.nix2
-rw-r--r--nixos/modules/profiles/clone-config.nix2
-rw-r--r--nixos/modules/profiles/demo.nix6
-rw-r--r--nixos/modules/profiles/docker-container.nix4
-rw-r--r--nixos/modules/profiles/installation-device.nix8
-rw-r--r--nixos/modules/profiles/minimal.nix5
-rw-r--r--nixos/modules/profiles/qemu-guest.nix2
-rw-r--r--nixos/modules/programs/adb.nix1
-rw-r--r--nixos/modules/programs/bash/bash.nix19
-rw-r--r--nixos/modules/programs/browserpass.nix1
-rw-r--r--nixos/modules/programs/ccache.nix83
-rw-r--r--nixos/modules/programs/chromium.nix8
-rw-r--r--nixos/modules/programs/criu.nix26
-rw-r--r--nixos/modules/programs/dconf.nix16
-rw-r--r--nixos/modules/programs/digitalbitbox/default.nix39
-rw-r--r--nixos/modules/programs/digitalbitbox/doc.xml85
-rw-r--r--nixos/modules/programs/iftop.nix18
-rw-r--r--nixos/modules/programs/info.nix30
-rw-r--r--nixos/modules/programs/less.nix131
-rw-r--r--nixos/modules/programs/man.nix31
-rw-r--r--nixos/modules/programs/plotinus.nix36
-rw-r--r--nixos/modules/programs/plotinus.xml25
-rw-r--r--nixos/modules/programs/rootston.nix103
-rw-r--r--nixos/modules/programs/shadow.nix10
-rw-r--r--nixos/modules/programs/singularity.nix20
-rw-r--r--nixos/modules/programs/ssh.nix2
-rw-r--r--nixos/modules/programs/sway.nix54
-rw-r--r--nixos/modules/programs/systemtap.nix28
-rw-r--r--nixos/modules/programs/tmux.nix18
-rw-r--r--nixos/modules/programs/udevil.nix14
-rw-r--r--nixos/modules/programs/way-cooler.nix78
-rw-r--r--nixos/modules/programs/yabar.nix149
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix16
-rw-r--r--nixos/modules/programs/zsh/zsh-autoenv.nix28
-rw-r--r--nixos/modules/programs/zsh/zsh.nix7
-rw-r--r--nixos/modules/rename.nix50
-rw-r--r--nixos/modules/security/acme.nix61
-rw-r--r--nixos/modules/security/audit.nix6
-rw-r--r--nixos/modules/security/duosec.nix4
-rw-r--r--nixos/modules/security/pam.nix50
-rw-r--r--nixos/modules/security/sudo.nix135
-rw-r--r--nixos/modules/security/wrappers/default.nix4
-rw-r--r--nixos/modules/security/wrappers/wrapper.c2
-rw-r--r--nixos/modules/services/audio/alsa.nix2
-rw-r--r--nixos/modules/services/audio/mopidy.nix14
-rw-r--r--nixos/modules/services/backup/almir.nix173
-rw-r--r--nixos/modules/services/backup/borgbackup.nix580
-rw-r--r--nixos/modules/services/backup/crashplan-small-business.nix74
-rw-r--r--nixos/modules/services/backup/duplicati.nix40
-rw-r--r--nixos/modules/services/backup/restic.nix150
-rw-r--r--nixos/modules/services/backup/tarsnap.nix69
-rw-r--r--nixos/modules/services/backup/znapzend.nix369
-rw-r--r--nixos/modules/services/cluster/kubernetes/dashboard.nix4
-rw-r--r--nixos/modules/services/cluster/kubernetes/default.nix22
-rw-r--r--nixos/modules/services/computing/slurm/slurm.nix31
-rw-r--r--nixos/modules/services/continuous-integration/buildkite-agent.nix181
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix13
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix9
-rw-r--r--nixos/modules/services/databases/4store-endpoint.nix2
-rw-r--r--nixos/modules/services/databases/4store.nix2
-rw-r--r--nixos/modules/services/databases/memcached.nix57
-rw-r--r--nixos/modules/services/databases/mysql.nix23
-rw-r--r--nixos/modules/services/databases/openldap.nix52
-rw-r--r--nixos/modules/services/databases/pgmanage.nix6
-rw-r--r--nixos/modules/services/databases/postgresql.nix7
-rw-r--r--nixos/modules/services/databases/redis.nix1
-rw-r--r--nixos/modules/services/desktops/gnome3/at-spi2-core.nix21
-rw-r--r--nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix27
-rw-r--r--nixos/modules/services/desktops/gnome3/evolution-data-server.nix6
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-keyring.nix4
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix4
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix6
-rw-r--r--nixos/modules/services/desktops/gnome3/tracker-miners.nix41
-rw-r--r--nixos/modules/services/desktops/pipewire.nix23
-rw-r--r--nixos/modules/services/desktops/telepathy.nix4
-rw-r--r--nixos/modules/services/editors/emacs.nix23
-rw-r--r--nixos/modules/services/games/factorio.nix20
-rw-r--r--nixos/modules/services/games/ghost-one.nix105
-rw-r--r--nixos/modules/services/hardware/acpid.nix40
-rw-r--r--nixos/modules/services/hardware/amd-hybrid-graphics.nix46
-rw-r--r--nixos/modules/services/hardware/bluetooth.nix12
-rw-r--r--nixos/modules/services/hardware/fwupd.nix90
-rw-r--r--nixos/modules/services/hardware/nvidia-optimus.nix2
-rw-r--r--nixos/modules/services/hardware/thinkfan.nix2
-rw-r--r--nixos/modules/services/hardware/trezord.nix2
-rw-r--r--nixos/modules/services/hardware/u2f.nix23
-rw-r--r--nixos/modules/services/hardware/udev.nix2
-rw-r--r--nixos/modules/services/hardware/usbmuxd.nix74
-rw-r--r--nixos/modules/services/logging/graylog.nix2
-rw-r--r--nixos/modules/services/logging/logcheck.nix2
-rw-r--r--nixos/modules/services/logging/logstash.nix2
-rw-r--r--nixos/modules/services/mail/clamsmtp.nix179
-rw-r--r--nixos/modules/services/mail/dkimproxy-out.nix118
-rw-r--r--nixos/modules/services/mail/dovecot.nix5
-rw-r--r--nixos/modules/services/mail/postfix.nix145
-rw-r--r--nixos/modules/services/mail/rspamd.nix268
-rw-r--r--nixos/modules/services/misc/defaultUnicornConfig.rb240
-rw-r--r--nixos/modules/services/misc/disnix.nix16
-rw-r--r--nixos/modules/services/misc/dysnomia.nix68
-rw-r--r--nixos/modules/services/misc/folding-at-home.nix2
-rw-r--r--nixos/modules/services/misc/geoip-updater.nix2
-rw-r--r--nixos/modules/services/misc/gitea.nix56
-rw-r--r--nixos/modules/services/misc/gitit.nix2
-rw-r--r--nixos/modules/services/misc/gitlab.nix26
-rw-r--r--nixos/modules/services/misc/gitolite.nix2
-rw-r--r--nixos/modules/services/misc/gitweb.nix59
-rw-r--r--nixos/modules/services/misc/gogs.nix3
-rw-r--r--nixos/modules/services/misc/gollum.nix21
-rw-r--r--nixos/modules/services/misc/home-assistant.nix144
-rw-r--r--nixos/modules/services/misc/ihaskell.nix2
-rw-r--r--nixos/modules/services/misc/logkeys.nix2
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix102
-rw-r--r--nixos/modules/services/misc/mbpfan.nix8
-rw-r--r--nixos/modules/services/misc/mesos-slave.nix4
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix69
-rw-r--r--nixos/modules/services/misc/nix-ssh-serve.nix24
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix18
-rw-r--r--nixos/modules/services/misc/novacomd.nix31
-rw-r--r--nixos/modules/services/misc/osrm.nix85
-rw-r--r--nixos/modules/services/misc/parsoid.nix4
-rw-r--r--nixos/modules/services/misc/safeeyes.nix50
-rw-r--r--nixos/modules/services/misc/serviio.nix92
-rw-r--r--nixos/modules/services/misc/ssm-agent.nix4
-rw-r--r--nixos/modules/services/misc/xmr-stak.nix73
-rw-r--r--nixos/modules/services/misc/zookeeper.nix11
-rw-r--r--nixos/modules/services/monitoring/apcupsd.nix2
-rw-r--r--nixos/modules/services/monitoring/fusion-inventory.nix3
-rw-r--r--nixos/modules/services/monitoring/grafana.nix20
-rw-r--r--nixos/modules/services/monitoring/monit.nix16
-rw-r--r--nixos/modules/services/monitoring/munin.nix68
-rw-r--r--nixos/modules/services/monitoring/netdata.nix54
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager.nix10
-rw-r--r--nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix68
-rw-r--r--nixos/modules/services/monitoring/prometheus/collectd-exporter.nix128
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix173
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.xml135
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix31
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/collectd.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix50
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix39
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/json.nix36
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/minio.nix65
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix31
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/node.nix39
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/postfix.nix81
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/snmp.nix71
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/unifi.nix67
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/varnish.nix21
-rw-r--r--nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix76
-rw-r--r--nixos/modules/services/monitoring/prometheus/json-exporter.nix74
-rw-r--r--nixos/modules/services/monitoring/prometheus/minio-exporter.nix117
-rw-r--r--nixos/modules/services/monitoring/prometheus/nginx-exporter.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/node-exporter.nix87
-rw-r--r--nixos/modules/services/monitoring/prometheus/snmp-exporter.nix127
-rw-r--r--nixos/modules/services/monitoring/prometheus/unifi-exporter.nix105
-rw-r--r--nixos/modules/services/monitoring/prometheus/varnish-exporter.nix61
-rw-r--r--nixos/modules/services/monitoring/smartd.nix20
-rw-r--r--nixos/modules/services/monitoring/statsd.nix31
-rw-r--r--nixos/modules/services/network-filesystems/beegfs.nix343
-rw-r--r--nixos/modules/services/network-filesystems/ceph.nix371
-rw-r--r--nixos/modules/services/network-filesystems/davfs2.nix74
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix14
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix99
-rw-r--r--nixos/modules/services/network-filesystems/openafs/client.nix239
-rw-r--r--nixos/modules/services/network-filesystems/openafs/lib.nix28
-rw-r--r--nixos/modules/services/network-filesystems/openafs/server.nix260
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix14
-rw-r--r--nixos/modules/services/network-filesystems/xtreemfs.nix2
-rw-r--r--nixos/modules/services/network-filesystems/yandex-disk.nix4
-rw-r--r--nixos/modules/services/networking/amuled.nix2
-rw-r--r--nixos/modules/services/networking/aria2.nix10
-rw-r--r--nixos/modules/services/networking/bird.nix23
-rw-r--r--nixos/modules/services/networking/connman.nix11
-rw-r--r--nixos/modules/services/networking/dante.nix2
-rw-r--r--nixos/modules/services/networking/ddclient.nix112
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix6
-rw-r--r--nixos/modules/services/networking/dhcpd.nix1
-rw-r--r--nixos/modules/services/networking/dnscache.nix31
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix6
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix10
-rw-r--r--nixos/modules/services/networking/firefox/sync-server.nix52
-rw-r--r--nixos/modules/services/networking/firewall.nix5
-rw-r--r--nixos/modules/services/networking/flashpolicyd.nix2
-rw-r--r--nixos/modules/services/networking/freeradius.nix72
-rw-r--r--nixos/modules/services/networking/gnunet.nix2
-rw-r--r--nixos/modules/services/networking/i2pd.nix17
-rw-r--r--nixos/modules/services/networking/iwd.nix2
-rw-r--r--nixos/modules/services/networking/kresd.nix48
-rw-r--r--nixos/modules/services/networking/lldpd.nix1
-rw-r--r--nixos/modules/services/networking/monero.nix238
-rw-r--r--nixos/modules/services/networking/mosquitto.nix16
-rw-r--r--nixos/modules/services/networking/murmur.nix3
-rw-r--r--nixos/modules/services/networking/nat.nix80
-rw-r--r--nixos/modules/services/networking/networkmanager.nix48
-rw-r--r--nixos/modules/services/networking/nftables.nix2
-rw-r--r--nixos/modules/services/networking/nix-serve.nix2
-rw-r--r--nixos/modules/services/networking/nixops-dns.nix79
-rw-r--r--nixos/modules/services/networking/nsd.nix6
-rw-r--r--nixos/modules/services/networking/openvpn.nix30
-rw-r--r--nixos/modules/services/networking/prosody.nix337
-rw-r--r--nixos/modules/services/networking/quagga.nix2
-rw-r--r--nixos/modules/services/networking/radvd.nix15
-rw-r--r--nixos/modules/services/networking/rdnssd.nix2
-rw-r--r--nixos/modules/services/networking/resilio.nix9
-rw-r--r--nixos/modules/services/networking/rxe.nix63
-rw-r--r--nixos/modules/services/networking/shadowsocks.nix112
-rw-r--r--nixos/modules/services/networking/softether.nix33
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix94
-rw-r--r--nixos/modules/services/networking/strongswan.nix23
-rw-r--r--nixos/modules/services/networking/stunnel.nix221
-rw-r--r--nixos/modules/services/networking/syncthing.nix30
-rw-r--r--nixos/modules/services/networking/tcpcrypt.nix10
-rw-r--r--nixos/modules/services/networking/tinc.nix2
-rw-r--r--nixos/modules/services/networking/tox-bootstrapd.nix2
-rw-r--r--nixos/modules/services/networking/unbound.nix2
-rw-r--r--nixos/modules/services/networking/zerotierone.nix22
-rw-r--r--nixos/modules/services/printing/cupsd.nix7
-rw-r--r--nixos/modules/services/scheduling/fcron.nix28
-rw-r--r--nixos/modules/services/search/elasticsearch.nix15
-rw-r--r--nixos/modules/services/security/clamav.nix13
-rw-r--r--nixos/modules/services/security/hologram-agent.nix7
-rw-r--r--nixos/modules/services/security/hologram-server.nix38
-rw-r--r--nixos/modules/services/security/physlock.nix64
-rw-r--r--nixos/modules/services/security/tor.nix138
-rw-r--r--nixos/modules/services/security/torify.nix2
-rw-r--r--nixos/modules/services/security/torsocks.nix2
-rw-r--r--nixos/modules/services/security/usbguard.nix4
-rw-r--r--nixos/modules/services/system/localtime.nix60
-rw-r--r--nixos/modules/services/torrent/transmission.nix22
-rw-r--r--nixos/modules/services/ttys/agetty.nix4
-rw-r--r--nixos/modules/services/web-apps/atlassian/jira.nix2
-rw-r--r--nixos/modules/services/web-apps/matomo-doc.xml (renamed from nixos/modules/services/web-apps/piwik-doc.xml)42
-rw-r--r--nixos/modules/services/web-apps/matomo.nix (renamed from nixos/modules/services/web-apps/piwik.nix)75
-rw-r--r--nixos/modules/services/web-apps/nexus.nix4
-rw-r--r--nixos/modules/services/web-apps/nixbot.nix149
-rw-r--r--nixos/modules/services/web-apps/pump.io-configure.js23
-rw-r--r--nixos/modules/services/web-apps/pump.io.nix438
-rw-r--r--nixos/modules/services/web-apps/restya-board.nix384
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix73
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/owncloud.nix48
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/per-server-options.nix2
-rw-r--r--nixos/modules/services/web-servers/lighttpd/default.nix3
-rw-r--r--nixos/modules/services/web-servers/lighttpd/gitweb.nix35
-rw-r--r--nixos/modules/services/web-servers/lighttpd/inginious.nix2
-rw-r--r--nixos/modules/services/web-servers/mighttpd2.nix132
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix28
-rw-r--r--nixos/modules/services/web-servers/nginx/gitweb.nix61
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix30
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix382
-rw-r--r--nixos/modules/services/web-servers/traefik.nix12
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix30
-rw-r--r--nixos/modules/services/x11/compton.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix12
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix24
-rw-r--r--nixos/modules/services/x11/desktop-managers/lxqt.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/mate.nix50
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix36
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix146
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix45
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix13
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix35
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix23
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/xpra.nix2
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix9
-rw-r--r--nixos/modules/services/x11/window-managers/2bwm.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/awesome.nix11
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix5
-rw-r--r--nixos/modules/services/x11/window-managers/evilwm.nix25
-rw-r--r--nixos/modules/services/x11/xautolock.nix28
-rw-r--r--nixos/modules/services/x11/xserver.nix46
-rw-r--r--nixos/modules/system/activation/activation-script.nix11
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl13
-rw-r--r--nixos/modules/system/activation/top-level.nix19
-rw-r--r--nixos/modules/system/boot/binfmt.nix139
-rw-r--r--nixos/modules/system/boot/grow-partition.nix50
-rw-r--r--nixos/modules/system/boot/initrd-network.nix18
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix5
-rw-r--r--nixos/modules/system/boot/kernel.nix30
-rw-r--r--nixos/modules/system/boot/kexec.nix37
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix16
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl2
-rw-r--r--nixos/modules/system/boot/luksroot.nix31
-rw-r--r--nixos/modules/system/boot/modprobe.nix2
-rw-r--r--nixos/modules/system/boot/networkd.nix4
-rw-r--r--nixos/modules/system/boot/plymouth.nix9
-rw-r--r--nixos/modules/system/boot/resolved.nix2
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh1
-rw-r--r--nixos/modules/system/boot/stage-1.nix56
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh9
-rw-r--r--nixos/modules/system/boot/stage-2.nix1
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix2
-rw-r--r--nixos/modules/system/boot/systemd.nix50
-rw-r--r--nixos/modules/tasks/filesystems/btrfs.nix113
-rw-r--r--nixos/modules/tasks/filesystems/exfat.nix2
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix33
-rw-r--r--nixos/modules/tasks/kbd.nix2
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix97
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix35
-rw-r--r--nixos/modules/tasks/network-interfaces.nix205
-rw-r--r--nixos/modules/testing/test-instrumentation.nix21
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix7
-rw-r--r--nixos/modules/virtualisation/amazon-init.nix2
-rw-r--r--nixos/modules/virtualisation/azure-agent.nix10
-rw-r--r--nixos/modules/virtualisation/brightbox-image.nix2
-rw-r--r--nixos/modules/virtualisation/container-config.nix2
-rw-r--r--nixos/modules/virtualisation/containers.nix27
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix50
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix18
-rw-r--r--nixos/modules/virtualisation/grow-partition.nix51
-rw-r--r--nixos/modules/virtualisation/hyperv-guest.nix37
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix31
-rw-r--r--nixos/modules/virtualisation/lxc.nix5
-rw-r--r--nixos/modules/virtualisation/lxcfs.nix4
-rw-r--r--nixos/modules/virtualisation/lxd.nix67
-rw-r--r--nixos/modules/virtualisation/nova-config.nix4
-rw-r--r--nixos/modules/virtualisation/nova.nix174
-rw-r--r--nixos/modules/virtualisation/openstack/common.nix84
-rw-r--r--nixos/modules/virtualisation/openstack/glance.nix245
-rw-r--r--nixos/modules/virtualisation/openstack/keystone.nix220
-rw-r--r--nixos/modules/virtualisation/openvswitch.nix2
-rw-r--r--nixos/modules/virtualisation/parallels-guest.nix74
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix20
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix2
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix12
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix37
-rw-r--r--nixos/release-combined.nix17
-rw-r--r--nixos/release-small.nix7
-rw-r--r--nixos/release.nix173
-rw-r--r--nixos/tests/atd.nix6
-rw-r--r--nixos/tests/beegfs.nix115
-rw-r--r--nixos/tests/bittorrent.nix6
-rw-r--r--nixos/tests/boot-stage1.nix1
-rw-r--r--nixos/tests/boot.nix7
-rw-r--r--nixos/tests/borgbackup.nix162
-rw-r--r--nixos/tests/ceph.nix140
-rw-r--r--nixos/tests/chromium.nix24
-rw-r--r--nixos/tests/cjdns.nix9
-rw-r--r--nixos/tests/cloud-init.nix1
-rw-r--r--nixos/tests/common/letsencrypt.nix24
-rw-r--r--nixos/tests/containers-bridge.nix4
-rw-r--r--nixos/tests/containers-extra_veth.nix6
-rw-r--r--nixos/tests/containers-hosts.nix6
-rw-r--r--nixos/tests/containers-imperative.nix4
-rw-r--r--nixos/tests/containers-macvlans.nix8
-rw-r--r--nixos/tests/containers-physical_interfaces.nix28
-rw-r--r--nixos/tests/containers-reloadable.nix2
-rw-r--r--nixos/tests/containers-restart_networking.nix14
-rw-r--r--nixos/tests/containers-tmpfs.nix2
-rw-r--r--nixos/tests/couchdb.nix56
-rw-r--r--nixos/tests/docker-tools-overlay.nix32
-rw-r--r--nixos/tests/docker-tools.nix49
-rw-r--r--nixos/tests/dovecot.nix19
-rw-r--r--nixos/tests/ec2.nix1
-rw-r--r--nixos/tests/elk.nix172
-rw-r--r--nixos/tests/ferm.nix8
-rw-r--r--nixos/tests/fwupd.nix19
-rw-r--r--nixos/tests/gjs.nix19
-rw-r--r--nixos/tests/glance.nix77
-rw-r--r--nixos/tests/gnome3-gdm.nix13
-rw-r--r--nixos/tests/grafana.nix2
-rw-r--r--nixos/tests/hocker-fetchdocker/default.nix15
-rw-r--r--nixos/tests/hocker-fetchdocker/hello-world-container.nix19
-rw-r--r--nixos/tests/hocker-fetchdocker/machine.nix26
-rw-r--r--nixos/tests/home-assistant.nix76
-rw-r--r--nixos/tests/iftop.nix30
-rw-r--r--nixos/tests/initrd-network-ssh/default.nix (renamed from nixos/tests/initrd-network-ssh.nix)29
-rw-r--r--nixos/tests/initrd-network-ssh/dropbear.privbin0 -> 1573 bytes
-rw-r--r--nixos/tests/initrd-network-ssh/dropbear.pub1
-rw-r--r--nixos/tests/initrd-network-ssh/generate-keys.nix12
-rw-r--r--nixos/tests/initrd-network-ssh/openssh.priv51
-rw-r--r--nixos/tests/initrd-network-ssh/openssh.pub1
-rw-r--r--nixos/tests/installer.nix86
-rw-r--r--nixos/tests/ipv6.nix2
-rw-r--r--nixos/tests/jenkins.nix5
-rw-r--r--nixos/tests/kernel-copperhead.nix4
-rw-r--r--nixos/tests/keymap.nix34
-rw-r--r--nixos/tests/keystone.nix82
-rw-r--r--nixos/tests/kubernetes/base.nix3
-rw-r--r--nixos/tests/kubernetes/certs.nix82
-rw-r--r--nixos/tests/kubernetes/dns.nix2
-rw-r--r--nixos/tests/kubernetes/e2e.nix2
-rw-r--r--nixos/tests/kubernetes/kubernetes-common.nix4
-rw-r--r--nixos/tests/kubernetes/rbac.nix4
-rw-r--r--nixos/tests/make-test.nix2
-rw-r--r--nixos/tests/matrix-synapse.nix30
-rw-r--r--nixos/tests/misc.nix18
-rw-r--r--nixos/tests/mutable-users.nix39
-rw-r--r--nixos/tests/nat.nix2
-rw-r--r--nixos/tests/netdata.nix31
-rw-r--r--nixos/tests/networking.nix211
-rw-r--r--nixos/tests/nix-ssh-serve.nix39
-rw-r--r--nixos/tests/novacomd.nix28
-rw-r--r--nixos/tests/nsd.nix22
-rw-r--r--nixos/tests/openldap.nix35
-rw-r--r--nixos/tests/openssh.nix17
-rw-r--r--nixos/tests/owncloud.nix39
-rw-r--r--nixos/tests/plotinus.nix27
-rw-r--r--nixos/tests/postgis.nix2
-rw-r--r--nixos/tests/powerdns.nix12
-rw-r--r--nixos/tests/predictable-interface-names.nix27
-rw-r--r--nixos/tests/printing.nix2
-rw-r--r--nixos/tests/prosody.nix75
-rw-r--r--nixos/tests/pump.io.nix89
-rw-r--r--nixos/tests/quagga.nix2
-rw-r--r--nixos/tests/radicale.nix2
-rw-r--r--nixos/tests/rspamd.nix140
-rw-r--r--nixos/tests/rxe.nix53
-rw-r--r--nixos/tests/ssh-keys.nix15
-rw-r--r--nixos/tests/statsd.nix40
-rw-r--r--nixos/tests/sudo.nix93
-rw-r--r--nixos/tests/switch-test.nix25
-rw-r--r--nixos/tests/systemd.nix68
-rw-r--r--nixos/tests/transmission.nix21
-rw-r--r--nixos/tests/vault.nix23
-rw-r--r--nixos/tests/virtualbox.nix6
-rw-r--r--nixos/tests/xautolock.nix24
-rw-r--r--nixos/tests/yabar.nix25
512 files changed, 16397 insertions, 6614 deletions
diff --git a/nixos/default.nix b/nixos/default.nix
index 0e45a1cd75e2..45da78e9261c 100644
--- a/nixos/default.nix
+++ b/nixos/default.nix
@@ -9,8 +9,6 @@ let
     modules = [ configuration ];
   };
 
-  inherit (eval) pkgs;
-
   # This is for `nixos-rebuild build-vm'.
   vmConfig = (import ./lib/eval-config.nix {
     inherit system;
@@ -30,7 +28,7 @@ let
 in
 
 {
-  inherit (eval) config options;
+  inherit (eval) pkgs config options;
 
   system = eval.config.system.build.toplevel;
 
diff --git a/nixos/doc/manual/configuration/firewall.xml b/nixos/doc/manual/configuration/firewall.xml
index 87406c28c2f7..75cccef95b38 100644
--- a/nixos/doc/manual/configuration/firewall.xml
+++ b/nixos/doc/manual/configuration/firewall.xml
@@ -23,10 +23,23 @@ networking.firewall.allowedTCPPorts = [ 80 443 ];
 </programlisting>
 
 Note that TCP port 22 (ssh) is opened automatically if the SSH daemon
-is enabled (<option>services.openssh.enable = true</option>).  UDP
+is enabled (<option>services.openssh.enable = true</option>). UDP
 ports can be opened through
-<option>networking.firewall.allowedUDPPorts</option>.  Also of
-interest is
+<option>networking.firewall.allowedUDPPorts</option>.</para>
+
+<para>To open ranges of TCP ports:
+
+<programlisting>
+networking.firewall.allowedTCPPortRanges = [
+  { from = 4000; to = 4007; }
+  { from = 8000; to = 8010; }
+];
+</programlisting>
+
+Similarly, UDP port ranges can be opened through
+<option>networking.firewall.allowedUDPPortRanges</option>.</para>
+
+<para>Also of interest is
 
 <programlisting>
 networking.firewall.allowPing = true;
diff --git a/nixos/doc/manual/configuration/ipv4-config.xml b/nixos/doc/manual/configuration/ipv4-config.xml
index 053501b1736d..68238b547d60 100644
--- a/nixos/doc/manual/configuration/ipv4-config.xml
+++ b/nixos/doc/manual/configuration/ipv4-config.xml
@@ -12,7 +12,7 @@ interfaces.  However, you can configure an interface manually as
 follows:
 
 <programlisting>
-networking.interfaces.eth0.ip4 = [ { address = "192.168.1.2"; prefixLength = 24; } ];
+networking.interfaces.eth0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
 </programlisting>
 
 Typically you’ll also want to set a default gateway and set of name
diff --git a/nixos/doc/manual/configuration/ipv6-config.xml b/nixos/doc/manual/configuration/ipv6-config.xml
index 6d9e0a164e9e..74a21e18ec3f 100644
--- a/nixos/doc/manual/configuration/ipv6-config.xml
+++ b/nixos/doc/manual/configuration/ipv6-config.xml
@@ -26,7 +26,7 @@ boot.kernel.sysctl."net.ipv6.conf.eth0.disable_ipv6" = true;
 DHCPv6. You can configure an interface manually:
 
 <programlisting>
-networking.interfaces.eth0.ip6 = [ { address = "fe00:aa:bb:cc::2"; prefixLength = 64; } ];
+networking.interfaces.eth0.ipv6.addresses = [ { address = "fe00:aa:bb:cc::2"; prefixLength = 64; } ];
 </programlisting>
 </para>
 
diff --git a/nixos/doc/manual/configuration/user-mgmt.xml b/nixos/doc/manual/configuration/user-mgmt.xml
index 2bd9cca5622f..c6656edff6c8 100644
--- a/nixos/doc/manual/configuration/user-mgmt.xml
+++ b/nixos/doc/manual/configuration/user-mgmt.xml
@@ -12,7 +12,7 @@ management.  In the declarative style, users are specified in
 states that a user account named <literal>alice</literal> shall exist:
 
 <programlisting>
-users.extraUsers.alice =
+users.users.alice =
   { isNormalUser = true;
     home = "/home/alice";
     description = "Alice Foobar";
@@ -34,7 +34,7 @@ to set a password, which is retained across invocations of
 
 <para>If you set users.mutableUsers to false, then the contents of /etc/passwd
 and /etc/group will be congruent to your NixOS configuration. For instance,
-if you remove a user from users.extraUsers and run nixos-rebuild, the user
+if you remove a user from users.users and run nixos-rebuild, the user
 account will cease to exist. Also, imperative commands for managing users
 and groups, such as useradd, are no longer available. Passwords may still be
 assigned by setting the user's <literal>hashedPassword</literal> option. A
@@ -54,7 +54,7 @@ to the user specification.</para>
 group named <literal>students</literal> shall exist:
 
 <programlisting>
-users.extraGroups.students.gid = 1000;
+users.groups.students.gid = 1000;
 </programlisting>
 
 As with users, the group ID (gid) is optional and will be assigned
@@ -68,8 +68,8 @@ account named <literal>alice</literal>:
 <screen>
 # useradd -m alice</screen>
 
-To make all nix tools available to this new user use `su - USER` which 
-opens a login shell (==shell that loads the profile) for given user. 
+To make all nix tools available to this new user use `su - USER` which
+opens a login shell (==shell that loads the profile) for given user.
 This will create the ~/.nix-defexpr symlink. So run:
 
 <screen>
diff --git a/nixos/doc/manual/configuration/x-windows.xml b/nixos/doc/manual/configuration/x-windows.xml
index fc6082ce3afd..9c2c59006f15 100644
--- a/nixos/doc/manual/configuration/x-windows.xml
+++ b/nixos/doc/manual/configuration/x-windows.xml
@@ -115,13 +115,14 @@ hardware.opengl.driSupport32Bit = true;
 <para>Support for Synaptics touchpads (found in many laptops such as
 the Dell Latitude series) can be enabled as follows:
 <programlisting>
-services.xserver.synaptics.enable = true;
+services.xserver.libinput.enable = true;
 </programlisting>
 The driver has many options (see <xref linkend="ch-options"/>).  For
-instance, the following enables two-finger scrolling:
+instance, the following disables tap-to-click behavior:
 <programlisting>
-services.xserver.synaptics.twoFingerScroll = true;
+services.xserver.libinput.tapping = false;
 </programlisting>
+Note: the use of <literal>services.xserver.synaptics</literal> is deprecated since NixOS 17.09.
 </para>
 
 </simplesect>
@@ -129,7 +130,7 @@ services.xserver.synaptics.twoFingerScroll = true;
 <simplesect><title>GTK/Qt themes</title>
 
 <para>GTK themes can be installed either to user profile or system-wide (via
-<literal>system.environmentPackages</literal>). To make Qt 5 applications look similar
+<literal>environment.systemPackages</literal>). To make Qt 5 applications look similar
 to GTK2 ones, you can install <literal>qt5.qtbase.gtk</literal> package into your
 system environment. It should work for all Qt 5 library versions.
 </para>
diff --git a/nixos/doc/manual/configuration/xfce.xml b/nixos/doc/manual/configuration/xfce.xml
index 21c7a85e19cc..18804d2c08be 100644
--- a/nixos/doc/manual/configuration/xfce.xml
+++ b/nixos/doc/manual/configuration/xfce.xml
@@ -35,18 +35,7 @@ services.compton = {
         To install them manually (system wide), put them into your
         <literal>environment.systemPackages</literal>.
     </para>
-
-    <para>
-        NixOS’s default <emphasis>display manager</emphasis> is SLiM.
-        (DM is the program that provides a graphical login prompt
-         and manages the X server.)
-        You can, for example, select KDE’s
-        <command>sddm</command> instead:
-        <programlisting>
-services.xserver.displayManager.sddm.enable = true;
-        </programlisting>
-    </para>
-
+         
     <simplesect>
         <title>Thunar Volume Support</title>
 
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 9413d71a34cf..ac22712baf87 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -6,22 +6,52 @@ let
   lib = pkgs.lib;
 
   # Remove invisible and internal options.
-  optionsList = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
+  optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
 
   # Replace functions by the string <function>
   substFunction = x:
     if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x
     else if builtins.isList x then map substFunction x
-    else if builtins.isFunction x then "<function>"
+    else if lib.isFunction x then "<function>"
     else x;
 
-  # Clean up declaration sites to not refer to the NixOS source tree.
-  optionsList' = lib.flip map optionsList (opt: opt // {
+  # Generate DocBook documentation for a list of packages. This is
+  # what `relatedPackages` option of `mkOption` from
+  # ../../../lib/options.nix influences.
+  #
+  # Each element of `relatedPackages` can be either
+  # - a string:  that will be interpreted as an attribute name from `pkgs`,
+  # - a list:    that will be interpreted as an attribute path from `pkgs`,
+  # - an attrset: that can specify `name`, `path`, `package`, `comment`
+  #   (either of `name`, `path` is required, the rest are optional).
+  genRelatedPackages = packages:
+    let
+      unpack = p: if lib.isString p then { name = p; }
+                  else if lib.isList p then { path = p; }
+                  else p;
+      describe = args:
+        let
+          name = args.name or (lib.concatStringsSep "." args.path);
+          path = args.path or [ args.name ];
+          package = args.package or (lib.attrByPath path (throw "Invalid package attribute path `${toString path}'") pkgs);
+        in "<listitem>"
+        + "<para><literal>pkgs.${name} (${package.meta.name})</literal>"
+        + lib.optionalString (!package.meta.available) " <emphasis>[UNAVAILABLE]</emphasis>"
+        + ": ${package.meta.description or "???"}.</para>"
+        + lib.optionalString (args ? comment) "\n<para>${args.comment}</para>"
+        # Lots of `longDescription's break DocBook, so we just wrap them into <programlisting>
+        + lib.optionalString (package.meta ? longDescription) "\n<programlisting>${package.meta.longDescription}</programlisting>"
+        + "</listitem>";
+    in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>";
+
+  optionsListDesc = lib.flip map optionsListVisible (opt: opt // {
+    # Clean up declaration sites to not refer to the NixOS source tree.
     declarations = map stripAnyPrefixes opt.declarations;
   }
   // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; }
   // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; }
-  // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; });
+  // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; }
+  // lib.optionalAttrs (opt ? relatedPackages) { relatedPackages = genRelatedPackages opt.relatedPackages; });
 
   # We need to strip references to /nix/store/* from options,
   # including any `extraSources` if some modules came from elsewhere,
@@ -32,8 +62,21 @@ let
   prefixesToStrip = map (p: "${toString p}/") ([ ../../.. ] ++ extraSources);
   stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip;
 
+  # Custom "less" that pushes up all the things ending in ".enable*"
+  # and ".package*"
+  optionLess = a: b:
+    let
+      ise = lib.hasPrefix "enable";
+      isp = lib.hasPrefix "package";
+      cmp = lib.splitByAndCompare ise lib.compare
+                                 (lib.splitByAndCompare isp lib.compare lib.compare);
+    in lib.compareLists cmp a.loc b.loc < 0;
+
+  # Customly sort option list for the man page.
+  optionsList = lib.sort optionLess optionsListDesc;
+
   # Convert the list of options into an XML file.
-  optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList');
+  optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList);
 
   optionsDocBook = runCommand "options-db.xml" {} ''
     optionsXML=${optionsXML}
@@ -44,7 +87,7 @@ let
       echo "for hints about the offending path)."
       exit 1
     fi
-    ${libxslt.bin}/bin/xsltproc \
+    ${buildPackages.libxslt.bin}/bin/xsltproc \
       --stringparam revision '${revision}' \
       -o $out ${./options-to-docbook.xsl} $optionsXML
   '';
@@ -81,11 +124,12 @@ let
   manualXsltprocOptions = toString [
     "--param section.autolabel 1"
     "--param section.label.includes.component.label 1"
-    "--stringparam html.stylesheet style.css"
+    "--stringparam html.stylesheet 'style.css overrides.css highlightjs/mono-blue.css'"
+    "--stringparam html.script './highlightjs/highlight.pack.js ./highlightjs/loader.js'"
     "--param xref.with.number.and.title 1"
     "--param toc.section.depth 3"
     "--stringparam admon.style ''"
-    "--stringparam callout.graphics.extension .gif"
+    "--stringparam callout.graphics.extension .svg"
     "--stringparam current.docid manual"
     "--param chunk.section.depth 0"
     "--param chunk.first.sections 1"
@@ -96,7 +140,7 @@ let
 
   manual-combined = runCommand "nixos-manual-combined"
     { inherit sources;
-      buildInputs = [ libxml2 libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
       meta.description = "The NixOS manual as plain docbook XML";
     }
     ''
@@ -106,13 +150,43 @@ let
       xmllint --xinclude --noxincludenode \
          --output ./man-pages-combined.xml ./man-pages.xml
 
-      xmllint --debug --noout --nonet \
-        --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \
-        manual-combined.xml
-      xmllint --debug --noout --nonet \
-        --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \
-        man-pages-combined.xml
-
+      # outputs the context of an xmllint error output
+      # LEN lines around the failing line are printed
+      function context {
+        # length of context
+        local LEN=6
+        # lines to print before error line
+        local BEFORE=4
+
+        # xmllint output lines are:
+        # file.xml:1234: there was an error on line 1234
+        while IFS=':' read -r file line rest; do
+          echo
+          if [[ -n "$rest" ]]; then
+            echo "$file:$line:$rest"
+            local FROM=$(($line>$BEFORE ? $line - $BEFORE : 1))
+            # number lines & filter context
+            nl --body-numbering=a "$file" | sed -n "$FROM,+$LEN p"
+          else
+            if [[ -n "$line" ]]; then
+              echo "$file:$line"
+            else
+              echo "$file"
+            fi
+          fi
+        done
+      }
+
+      function lintrng {
+        xmllint --debug --noout --nonet \
+          --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \
+          "$1" \
+          2>&1 | context 1>&2
+          # ^ redirect assumes xmllint doesn’t print to stdout
+      }
+
+      lintrng manual-combined.xml
+      lintrng man-pages-combined.xml
 
       mkdir $out
       cp manual-combined.xml $out/
@@ -121,7 +195,7 @@ let
 
   olinkDB = runCommand "manual-olinkdb"
     { inherit sources;
-      buildInputs = [ libxml2 libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
     }
     ''
       xsltproc \
@@ -161,7 +235,7 @@ in rec {
       mkdir -p $dst
 
       cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON
-        (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList'))))
+        (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList))))
       } $dst/options.json
 
       mkdir -p $out/nix-support
@@ -171,7 +245,7 @@ in rec {
   # Generate the NixOS manual.
   manual = runCommand "nixos-manual"
     { inherit sources;
-      buildInputs = [ libxml2 libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
       meta.description = "The NixOS manual in HTML format";
       allowedReferences = ["out"];
     }
@@ -187,9 +261,11 @@ in rec {
         ${manual-combined}/manual-combined.xml
 
       mkdir -p $dst/images/callouts
-      cp ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/images/callouts/
+      cp ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.svg $dst/images/callouts/
 
-      cp ${./style.css} $dst/style.css
+      cp ${../../../doc/style.css} $dst/style.css
+      cp ${../../../doc/overrides.css} $dst/overrides.css
+      cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
 
       mkdir -p $out/nix-support
       echo "nix-build out $out" >> $out/nix-support/hydra-build-products
@@ -199,7 +275,7 @@ in rec {
 
   manualEpub = runCommand "nixos-manual-epub"
     { inherit sources;
-      buildInputs = [ libxml2 libxslt zip ];
+      buildInputs = [ libxml2.bin libxslt.bin zip ];
     }
     ''
       # Generate the epub manual.
@@ -213,7 +289,7 @@ in rec {
         ${manual-combined}/manual-combined.xml
 
       mkdir -p $dst/epub/OEBPS/images/callouts
-      cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/epub/OEBPS/images/callouts # */
+      cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.svg $dst/epub/OEBPS/images/callouts # */
       echo "application/epub+zip" > mimetype
       manual="$dst/nixos-manual.epub"
       zip -0Xq "$manual" mimetype
@@ -229,7 +305,7 @@ in rec {
   # Generate the NixOS manpages.
   manpages = runCommand "nixos-manpages"
     { inherit sources;
-      buildInputs = [ libxml2 libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
       allowedReferences = ["out"];
     }
     ''
diff --git a/nixos/doc/manual/development/option-declarations.xml b/nixos/doc/manual/development/option-declarations.xml
index be793152f9ef..ed718c89eb77 100644
--- a/nixos/doc/manual/development/option-declarations.xml
+++ b/nixos/doc/manual/development/option-declarations.xml
@@ -22,6 +22,15 @@ options = {
 };
 </programlisting>
 
+The attribute names within the <replaceable>name</replaceable>
+attribute path must be camel cased in general but should, as an
+exception, match the
+<link
+xlink:href="https://nixos.org/nixpkgs/manual/#sec-package-naming">
+package attribute name</link> when referencing a Nixpkgs package. For
+example, the option <varname>services.nix-serve.bindAddress</varname>
+references the <varname>nix-serve</varname> Nixpkgs package.
+
 </para>
 
 <para>The function <varname>mkOption</varname> accepts the following arguments.
diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml
index 83dcf0232d9d..13fa8d1e114c 100644
--- a/nixos/doc/manual/development/option-types.xml
+++ b/nixos/doc/manual/development/option-types.xml
@@ -110,6 +110,12 @@
     <listitem><para>A string. Multiple definitions are concatenated with a 
         collon <literal>":"</literal>.</para></listitem>
   </varlistentry>
+  <varlistentry>
+    <term><varname>types.strMatching</varname></term>
+    <listitem><para>A string matching a specific regular expression. Multiple
+    definitions cannot be merged.  The regular expression is processed using
+    <literal>builtins.match</literal>.</para></listitem>
+  </varlistentry>
 </variablelist>
 
  </section>
@@ -276,8 +282,8 @@ options.mod = mkOption {
     option set (<xref linkend='ex-submodule-listof-definition' />).</para>
     
 
-<example xml:id='ex-submodule-listof-declaration'><title>Declaration of a list 
-    nof submodules</title>
+<example xml:id='ex-submodule-listof-declaration'><title>Declaration of a list
+    of submodules</title>
 <screen>
 options.mod = mkOption {
   description = "submodule example";
diff --git a/nixos/doc/manual/development/testing-installer.xml b/nixos/doc/manual/development/testing-installer.xml
index 20c8d51815ad..16bc8125d9ff 100644
--- a/nixos/doc/manual/development/testing-installer.xml
+++ b/nixos/doc/manual/development/testing-installer.xml
@@ -11,15 +11,17 @@ tedious, so here is a quick way to see if the installer works
 properly:
 
 <screen>
-$ nix-build -A config.system.build.nixos-install
 # mount -t tmpfs none /mnt
+# nixos-generate-config --root /mnt
+$ nix-build '&lt;nixpkgs/nixos>' -A config.system.build.nixos-install
 # ./result/bin/nixos-install</screen>
 
 To start a login shell in the new NixOS installation in
 <filename>/mnt</filename>:
 
 <screen>
-# ./result/bin/nixos-install --chroot
+$ nix-build '&lt;nixpkgs/nixos>' -A config.system.build.nixos-enter
+# ./result/bin/nixos-enter
 </screen>
 
 </para>
diff --git a/nixos/doc/manual/development/writing-nixos-tests.xml b/nixos/doc/manual/development/writing-nixos-tests.xml
index b9da712b86f0..a8f6aa00858e 100644
--- a/nixos/doc/manual/development/writing-nixos-tests.xml
+++ b/nixos/doc/manual/development/writing-nixos-tests.xml
@@ -262,8 +262,47 @@ startAll;
     <literal>waitForWindow(qr/Terminal/)</literal>.</para></listitem>
   </varlistentry>
 
+  <varlistentry>
+    <term><methodname>copyFileFromHost</methodname></term>
+    <listitem><para>Copies a file from host to machine, e.g.,
+    <literal>copyFileFromHost("myfile", "/etc/my/important/file")</literal>.</para>
+    <para>The first argument is the file on the host. The file needs to be
+    accessible while building the nix derivation. The second argument is
+    the location of the file on the machine.</para>
+    </listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>systemctl</methodname></term>
+    <listitem>
+      <para>Runs <literal>systemctl</literal> commands with optional support for
+      <literal>systemctl --user</literal></para>
+    <para>
+      <programlisting>
+        $machine->systemctl("list-jobs --no-pager"); // runs `systemctl list-jobs --no-pager`
+        $machine->systemctl("list-jobs --no-pager", "any-user"); // spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager`
+      </programlisting>
+    </para>
+    </listitem>
+  </varlistentry>
+
 </variablelist>
 
 </para>
 
-</section>
\ No newline at end of file
+<para>
+  To test user units declared by <literal>systemd.user.services</literal> the optional <literal>$user</literal>
+  argument can be used:
+
+  <programlisting>
+    $machine->start;
+    $machine->waitForX;
+    $machine->waitForUnit("xautolock.service", "x-session-user");
+  </programlisting>
+
+  This applies to <literal>systemctl</literal>, <literal>getUnitInfo</literal>,
+  <literal>waitForUnit</literal>, <literal>startJob</literal>
+  and <literal>stopJob</literal>.
+</para>
+
+</section>
diff --git a/nixos/doc/manual/installation/changing-config.xml b/nixos/doc/manual/installation/changing-config.xml
index 75df307a1b7c..4db9020b9606 100644
--- a/nixos/doc/manual/installation/changing-config.xml
+++ b/nixos/doc/manual/installation/changing-config.xml
@@ -70,9 +70,21 @@ $ ./result/bin/run-*-vm
 </screen>
 
 The VM does not have any data from your host system, so your existing
-user accounts and home directories will not be available.  You can
-forward ports on the host to the guest.  For instance, the following
-will forward host port 2222 to guest port 22 (SSH):
+user accounts and home directories will not be available unless you
+have set <literal>mutableUsers = false</literal>.  Another way is to
+temporarily add the following to your configuration:
+
+<screen>
+users.extraUsers.your-user.initialPassword = "test"  
+</screen>
+
+<emphasis>Important:</emphasis> delete the $hostname.qcow2 file if you
+have started the virtual machine at least once without the right
+users, otherwise the changes will not get picked up.
+
+You can forward ports on the host to the guest.  For
+instance, the following will forward host port 2222 to guest port 22
+(SSH):
 
 <screen>
 $ QEMU_NET_OPTS="hostfwd=tcp::2222-:22" ./result/bin/run-*-vm
diff --git a/nixos/doc/manual/installation/installing-from-other-distro.xml b/nixos/doc/manual/installation/installing-from-other-distro.xml
new file mode 100644
index 000000000000..ecd020a067a9
--- /dev/null
+++ b/nixos/doc/manual/installation/installing-from-other-distro.xml
@@ -0,0 +1,309 @@
+<!-- vim: set expandtab ts=2 softtabstop=2 shiftwidth=2 smarttab textwidth=80 wrapmargin=2 -->
+<section
+    xmlns="http://docbook.org/ns/docbook"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:xi="http://www.w3.org/2001/XInclude"
+    version="5.0"
+    xml:id="sec-installing-from-other-distro">
+
+    <title>Installing from another Linux distribution</title>
+
+    <para>
+        Because Nix (the package manager) &amp; Nixpkgs (the Nix packages
+        collection) can both be installed on any (most?) Linux distributions,
+        they can be used to install NixOS in various creative ways. You can,
+        for instance:
+    </para>
+
+    <orderedlist>
+        <listitem><para>Install NixOS on another partition, from your existing
+                Linux distribution (without the use of a USB or optical
+                device!)</para></listitem>
+
+        <listitem><para>Install NixOS on the same partition (in place!), from
+                your existing non-NixOS Linux distribution using
+                <literal>NIXOS_LUSTRATE</literal>.</para></listitem>
+
+        <listitem><para>Install NixOS on your hard drive from the Live CD of
+                any Linux distribution.</para></listitem>
+    </orderedlist>
+
+    <para>The first steps to all these are the same:</para>
+
+    <orderedlist>
+        <listitem>
+            <para>Install the Nix package manager:</para>
+
+            <para>Short version:</para>
+
+            <screen>
+$ bash &lt;(curl https://nixos.org/nix/install)
+$ . $HOME/.nix-profile/etc/profile.d/nix.sh # …or open a fresh shell</screen>
+
+            <para>More details in the <link
+                    xlink:href="https://nixos.org/nix/manual/#chap-quick-start">
+                    Nix manual</link></para>
+        </listitem>
+
+        <listitem>
+            <para>Switch to the NixOS channel:</para>
+
+            <para>If you've just installed Nix on a non-NixOS distribution, you
+                will be on the <literal>nixpkgs</literal> channel by
+                default.</para>
+
+            <screen>
+$ nix-channel --list
+nixpkgs https://nixos.org/channels/nixpkgs-unstable</screen>
+
+            <para>As that channel gets released without running the NixOS
+                tests, it will be safer to use the <literal>nixos-*</literal>
+                channels instead:</para>
+
+            <screen>
+$ nix-channel --add https://nixos.org/channels/nixos-<replaceable>version</replaceable> nixpkgs</screen>
+
+            <para>You may want to throw in a <literal>nix-channel
+                    --update</literal> for good measure.</para>
+        </listitem>
+
+        <listitem>
+            <para>Install the NixOS installation tools:</para>
+
+            <para>You'll need <literal>nixos-generate-config</literal> and
+                <literal>nixos-install</literal> and we'll throw in some man
+                pages and <literal>nixos-enter</literal> just in case you want
+                to chroot into your NixOS partition. They are installed by
+                default on NixOS, but you don't have NixOS yet..</para>
+
+            <screen>$ nix-env -iE "_: with import &lt;nixpkgs/nixos&gt; { configuration = {}; }; with config.system.build; [ nixos-generate-config nixos-install nixos-enter manual.manpages ]"</screen>
+        </listitem>
+
+        <listitem>
+            <note><para>The following 5 steps are only for installing NixOS to
+                    another partition. For installing NixOS in place using
+                    <literal>NIXOS_LUSTRATE</literal>, skip ahead.</para></note>
+
+            <para>Prepare your target partition:</para>
+
+            <para>At this point it is time to prepare your target partition.
+                Please refer to the partitioning, file-system creation, and
+                mounting steps of <xref linkend="sec-installation" /></para>
+
+            <para>If you're about to install NixOS in place using
+                <literal>NIXOS_LUSTRATE</literal> there is nothing to do for
+                this step.</para>
+        </listitem>
+
+        <listitem>
+            <para>Generate your NixOS configuration:</para>
+
+            <screen>$ sudo `which nixos-generate-config` --root /mnt</screen>
+
+            <para>You'll probably want to edit the configuration files. Refer
+                to the <literal>nixos-generate-config</literal> step in <xref
+                    linkend="sec-installation" /> for more information.</para>
+
+            <para>Consider setting up the NixOS bootloader to give you the
+                ability to boot on your existing Linux partition. For instance,
+                if you're using GRUB and your existing distribution is running
+                Ubuntu, you may want to add something like this to your
+                <literal>configuration.nix</literal>:</para>
+
+            <programlisting>
+boot.loader.grub.extraEntries = ''
+  menuentry "Ubuntu" {
+    search --set=ubuntu --fs-uuid 3cc3e652-0c1f-4800-8451-033754f68e6e
+    configfile "($ubuntu)/boot/grub/grub.cfg"
+  }
+'';</programlisting>
+
+            <para>(You can find the appropriate UUID for your partition in
+                <literal>/dev/disk/by-uuid</literal>)</para>
+        </listitem>
+
+        <listitem>
+            <para>Create the <literal>nixbld</literal> group and user on your
+                original distribution:</para>
+
+            <screen>
+$ sudo groupadd -g 30000 nixbld
+$ sudo useradd -u 30000 -g nixbld -G nixbld nixbld</screen>
+        </listitem>
+
+        <listitem>
+            <para>Download/build/install NixOS:</para>
+
+            <warning><para>Once you complete this step, you might no longer be
+                    able to boot on existing systems without the help of a
+                    rescue USB drive or similar.</para></warning>
+
+            <screen>$ sudo PATH="$PATH" NIX_PATH="$NIX_PATH" `which nixos-install` --root /mnt</screen>
+
+            <para>Again, please refer to the <literal>nixos-install</literal>
+                step in <xref linkend="sec-installation" /> for more
+                information.</para>
+
+            <para>That should be it for installation to another partition!</para>
+        </listitem>
+
+        <listitem>
+            <para>Optionally, you may want to clean up your non-NixOS distribution:</para>
+
+            <screen>
+$ sudo userdel nixbld
+$ sudo groupdel nixbld</screen>
+
+            <para>If you do not wish to keep the Nix package mananager
+                installed either, run something like <literal>sudo rm -rv
+                    ~/.nix-* /nix</literal> and remove the line that the Nix
+                installer added to your <literal>~/.profile</literal>.</para>
+        </listitem>
+
+        <listitem>
+            <note><para>The following steps are only for installing NixOS in
+                    place using
+                    <literal>NIXOS_LUSTRATE</literal>:</para></note>
+
+            <para>Generate your NixOS configuration:</para>
+
+            <screen>$ sudo `which nixos-generate-config` --root /</screen>
+
+            <para>Note that this will place the generated configuration files
+                in <literal>/etc/nixos</literal>. You'll probably want to edit
+                the configuration files. Refer to the
+                <literal>nixos-generate-config</literal> step in <xref
+                    linkend="sec-installation" /> for more information.</para>
+
+            <para>You'll likely want to set a root password for your first boot
+                using the configuration files because you won't have a chance
+                to enter a password until after you reboot. You can initalize
+                the root password to an empty one with this line: (and of course
+                don't forget to set one once you've rebooted or to lock the
+                account with <literal>sudo passwd -l root</literal> if you use
+                <literal>sudo</literal>)</para>
+
+            <programlisting>users.extraUsers.root.initialHashedPassword = "";</programlisting>
+        </listitem>
+
+        <listitem>
+            <para>Build the NixOS closure and install it in the
+                <literal>system</literal> profile:</para>
+
+            <screen>$ nix-env -p /nix/var/nix/profiles/system -f '&lt;nixpkgs/nixos&gt;' -I nixos-config=/etc/nixos/configuration.nix -iA system</screen>
+        </listitem>
+
+        <listitem>
+            <para>Change ownership of the <literal>/nix</literal> tree to root
+                (since your Nix install was probably single user):</para>
+
+            <screen>$ sudo chown -R 0.0 /nix</screen>
+        </listitem>
+
+        <listitem>
+            <para>Set up the <literal>/etc/NIXOS</literal> and
+                <literal>/etc/NIXOS_LUSTRATE</literal> files:</para>
+
+            <para><literal>/etc/NIXOS</literal> officializes that this is now a
+                NixOS partition (the bootup scripts require its presence).</para>
+
+            <para><literal>/etc/NIXOS_LUSTRATE</literal> tells the NixOS bootup
+                scripts to move <emphasis>everything</emphasis> that's in the
+                root partition to <literal>/old-root</literal>. This will move
+                your existing distribution out of the way in the very early
+                stages of the NixOS bootup. There are exceptions (we do need to
+                keep NixOS there after all), so the NixOS lustrate process will
+                not touch:</para>
+
+            <itemizedlist>
+                <listitem><para>The <literal>/nix</literal>
+                        directory</para></listitem>
+
+                <listitem><para>The <literal>/boot</literal>
+                        directory</para></listitem>
+
+                <listitem><para>Any file or directory listed in
+                        <literal>/etc/NIXOS_LUSTRATE</literal> (one per
+                        line)</para></listitem>
+            </itemizedlist>
+
+            <note><para>Support for <literal>NIXOS_LUSTRATE</literal> was added
+                    in NixOS 16.09. The act of "lustrating" refers to the
+                    wiping of the existing distribution. Creating
+                    <literal>/etc/NIXOS_LUSTRATE</literal> can also be used on
+                    NixOS to remove all mutable files from your root partition
+                    (anything that's not in <literal>/nix</literal> or
+                    <literal>/boot</literal> gets "lustrated" on the next
+                    boot.</para>
+                <para>lustrate /ˈlʌstreɪt/ verb.</para>
+                <para>purify by expiatory sacrifice, ceremonial washing, or
+                    some other ritual action.</para></note>
+
+            <para>Let's create the files:</para>
+
+            <screen>
+$ sudo touch /etc/NIXOS
+$ sudo touch /etc/NIXOS_LUSTRATE</screen>
+
+            <para>Let's also make sure the NixOS configuration files are kept
+                once we reboot on NixOS:</para>
+
+            <screen>
+$ echo etc/nixos | sudo tee -a /etc/NIXOS_LUSTRATE</screen>
+        </listitem>
+
+        <listitem>
+            <para>Finally, move the <literal>/boot</literal> directory of your
+                current distribution out of the way (the lustrate process will
+                take care of the rest once you reboot, but this one must be
+                moved out now because NixOS needs to install its own boot
+                files:</para>
+
+            <warning><para>Once you complete this step, your current
+                    distribution will no longer be bootable! If you didn't get
+                    all the NixOS configuration right, especially those
+                    settings pertaining to boot loading and root partition,
+                    NixOS may not be bootable either. Have a USB rescue device
+                    ready in case this happens. </para></warning>
+
+            <screen>
+$ sudo mv -v /boot /boot.bak &amp;&amp;
+    sudo /nix/var/nix/profiles/system/bin/switch-to-configuration boot</screen>
+
+            <para>Cross your fingers, reboot, hopefully you should get a NixOS
+                prompt!</para>
+        </listitem>
+        <listitem>
+            <para>If for some reason you want to revert to the old
+                distribution, you'll need to boot on a USB rescue disk and do
+                something along these lines:</para>
+
+            <screen>
+# mkdir root
+# mount /dev/sdaX root
+# mkdir root/nixos-root
+# mv -v root/* root/nixos-root/
+# mv -v root/nixos-root/old-root/* root/
+# mv -v root/boot.bak root/boot  # We had renamed this by hand earlier
+# umount root
+# reboot</screen>
+
+            <para>This may work as is or you might also need to reinstall the
+                boot loader</para>
+
+            <para>And of course, if you're happy with NixOS and no longer need
+                the old distribution:</para>
+
+            <screen>sudo rm -rf /old-root</screen>
+        </listitem>
+
+        <listitem>
+            <para>It's also worth noting that this whole process can be
+                automated. This is especially useful for Cloud VMs, where
+                provider do not provide NixOS. For instance, <link
+                    xlink:href="https://github.com/elitak/nixos-infect">nixos-infect</link>
+                uses the lustrate process to convert Digital Ocean droplets to
+                NixOS from other distributions automatically.</para>
+        </listitem>
+    </orderedlist>
+</section>
diff --git a/nixos/doc/manual/installation/installing-uefi.xml b/nixos/doc/manual/installation/installing-uefi.xml
deleted file mode 100644
index 0d3eaa8bb1fb..000000000000
--- a/nixos/doc/manual/installation/installing-uefi.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="sec-uefi-installation">
-
-<title>UEFI Installation</title>
-
-<para>NixOS can also be installed on UEFI systems.  The procedure
-is by and large the same as a BIOS installation, with the following
-changes:
-
-<itemizedlist>
-  <listitem>
-    <para>You should boot the live CD in UEFI mode (consult your
-    specific hardware's documentation for instructions). You may find
-    the <link
-    xlink:href="http://www.rodsbooks.com/refind">rEFInd
-    boot manager</link> useful.</para>
-  </listitem>
-  <listitem>
-    <para>Instead of <command>fdisk</command>, you should use
-    <command>gdisk</command> to partition your disks. You will need to
-    have a separate partition for <filename>/boot</filename> with
-    partition code EF00, and it should be formatted as a
-    <literal>vfat</literal> filesystem.</para>
-  </listitem>
-  <listitem>
-    <para>Instead of <option>boot.loader.grub.device</option>,
-    you must set <option>boot.loader.systemd-boot.enable</option> to
-    <literal>true</literal>. <command>nixos-generate-config</command>
-    should do this automatically for new configurations when booted in
-    UEFI mode.</para>
-  </listitem>
-  <listitem>
-    <para>After having mounted your installation partition to
-    <code>/mnt</code>, you must mount the <code>boot</code> partition
-    to <code>/mnt/boot</code>.</para>
-  </listitem>
-  <listitem>
-    <para>You may want to look at the options starting with
-    <option>boot.loader.efi</option> and <option>boot.loader.systemd-boot</option>
-    as well.</para>
-  </listitem>
-</itemizedlist>
-</para>
-
-</section>
diff --git a/nixos/doc/manual/installation/installing-usb.xml b/nixos/doc/manual/installation/installing-usb.xml
index 31d51816e39b..d68cd6162632 100644
--- a/nixos/doc/manual/installation/installing-usb.xml
+++ b/nixos/doc/manual/installation/installing-usb.xml
@@ -11,10 +11,24 @@ a USB stick. You can use the <command>dd</command> utility to write the image:
 <command>dd if=<replaceable>path-to-image</replaceable>
 of=<replaceable>/dev/sdb</replaceable></command>. Be careful about specifying the
 correct drive; you can use the <command>lsblk</command> command to get a list of
-block devices. If you're on macOS you can run <command>diskutil list</command>
-to see the list of devices; the device you'll use for the USB must be ejected
-before writing the image.</para>
+block devices.</para>
 
+<para>On macOS:
+<programlisting>
+$ diskutil list
+[..]
+/dev/diskN (external, physical):
+   #:                       TYPE NAME                    SIZE       IDENTIFIER
+[..]
+$ diskutil unmountDisk diskN
+Unmount of all volumes on diskN was successful
+$ sudo dd bs=1m if=nix.iso of=/dev/rdiskN
+</programlisting>
+Using the 'raw' <command>rdiskN</command> device instead of <command>diskN</command>
+completes in minutes instead of hours. After <command>dd</command> completes, a GUI
+dialog "The disk you inserted was not readable by this computer" will pop up, which
+can be ignored.</para>
+         
 <para>The <command>dd</command> utility will write the image verbatim to the drive,
 making it the recommended option for both UEFI and non-UEFI installations. For
 non-UEFI installations, you can alternatively use
@@ -37,7 +51,7 @@ ISO, copy its contents verbatim to your drive, then either:
   <listitem>
     <para>If you want to load the contents of the ISO to ram after bootin
     (So you can remove the stick after bootup) you can append the parameter
-    <literal>copytoram</literal>to the <literal>options</literal> field.</para>
+    <literal>copytoram</literal> to the <literal>options</literal> field.</para>
   </listitem>
 </itemizedlist>
 </para>
diff --git a/nixos/doc/manual/installation/installing-virtualbox-guest.xml b/nixos/doc/manual/installation/installing-virtualbox-guest.xml
index ee9680ced397..7fcd22a112cf 100644
--- a/nixos/doc/manual/installation/installing-virtualbox-guest.xml
+++ b/nixos/doc/manual/installation/installing-virtualbox-guest.xml
@@ -4,18 +4,18 @@
          version="5.0"
          xml:id="sec-instaling-virtualbox-guest">
 
-<title>Installing in a Virtualbox guest</title>
+<title>Installing in a VirtualBox guest</title>
 <para>
-  Installing NixOS into a Virtualbox guest is convenient for users who want to
+  Installing NixOS into a VirtualBox guest is convenient for users who want to
   try NixOS without installing it on bare metal. If you want to use a pre-made
-  Virtualbox appliance, it is available at <link
+  VirtualBox appliance, it is available at <link
   xlink:href="https://nixos.org/nixos/download.html">the downloads page</link>.
-  If you want to set up a Virtualbox guest manually, follow these instructions:
+  If you want to set up a VirtualBox guest manually, follow these instructions:
 </para>
 
 <orderedlist>
 
-  <listitem><para>Add a New Machine in Virtualbox with OS Type "Linux / Other
+  <listitem><para>Add a New Machine in VirtualBox with OS Type "Linux / Other
   Linux"</para></listitem>
 
   <listitem><para>Base Memory Size: 768 MB or higher.</para></listitem>
diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml
index b0674307a563..1f09704bce53 100644
--- a/nixos/doc/manual/installation/installing.xml
+++ b/nixos/doc/manual/installation/installing.xml
@@ -6,9 +6,18 @@
 
 <title>Installing NixOS</title>
 
+<para>NixOS can be installed on BIOS or UEFI systems.  The procedure
+for a UEFI installation is by and large the same as a BIOS installation. The differences are mentioned in the steps that follow.</para>
+
 <orderedlist>
 
-  <listitem><para>Boot from the CD.</para></listitem>
+  <listitem><para>Boot from the CD.</para>
+    <variablelist>
+      <varlistentry><term>UEFI systems</term>
+      <listitem><para>You should boot the live CD in UEFI mode
+  (consult your specific hardware's documentation for instructions).
+  You may find the <link xlink:href="http://www.rodsbooks.com/refind">rEFInd boot
+  manager</link> useful.</para></listitem></varlistentry></variablelist></listitem>
 
   <listitem><para>The CD contains a basic NixOS installation.  (It
   also contains Memtest86+, useful if you want to test new hardware).
@@ -36,7 +45,10 @@
   using <command>ifconfig</command>.</para>
   <para>To manually configure the network on the graphical installer,
   first disable network-manager with
-  <command>systemctl stop network-manager</command>.</para></listitem>
+  <command>systemctl stop network-manager</command>.</para>
+  <para>To manually configure the wifi on the minimal installer, run
+  <command>wpa_supplicant -B -i interface -c &lt;(wpa_passphrase 'SSID' 'key')</command>.</para></listitem>
+
 
   <listitem><para>If you would like to continue the installation from a different
   machine you need to activate the SSH daemon via <literal>systemctl start sshd</literal>.
@@ -50,7 +62,31 @@
   <itemizedlist>
 
     <listitem><para>For partitioning:
-    <command>fdisk</command>.</para></listitem>
+    <command>fdisk</command>.
+<screen>
+# fdisk /dev/sda # <lineannotation>(or whatever device you want to install on)</lineannotation>
+-- for UEFI systems only
+> n      # <lineannotation>(create a new partition for /boot)</lineannotation>
+> 3      # <lineannotation>(make it a partition number 3)</lineannotation>
+>        # <lineannotation>(press enter to accept the default)</lineannotation>
+> +512M  # <lineannotation>(the size of the UEFI boot partition)</lineannotation>
+> t      # <lineannotation>(change the partition type ...)</lineannotation>
+> 3      # <lineannotation>(... of the boot partition ...)</lineannotation>
+> 1      # <lineannotation>(... to 'UEFI System')</lineannotation>
+-- for BIOS or UEFI systems
+> n      # <lineannotation>(create a new partition for /swap)</lineannotation>
+> 2      # <lineannotation>(make it a partition number 2)</lineannotation>
+>        # <lineannotation>(press enter to accept the default)</lineannotation>
+> +8G    # <lineannotation>(the size of the swap partition, set to whatever you like)</lineannotation>
+> n      # <lineannotation>(create a new partition for /)</lineannotation>
+> 1      # <lineannotation>(make it a partition number 1)</lineannotation>
+>        # <lineannotation>(press enter to accept the default)</lineannotation>
+>        # <lineannotation>(press enter to accept the default and use the rest of the remaining space)</lineannotation>
+> a      # <lineannotation>(make the partition bootable)</lineannotation>
+> x      # <lineannotation>(enter expert mode)</lineannotation>
+> f      # <lineannotation>(fix up the partition ordering)</lineannotation>
+> r      # <lineannotation>(exit expert mode)</lineannotation>
+> w      # <lineannotation>(write the partition table to disk and exit)</lineannotation></screen></para></listitem>
 
     <listitem><para>For initialising Ext4 partitions:
     <command>mkfs.ext4</command>.  It is recommended that you assign a
@@ -67,18 +103,30 @@
     <listitem><para>For creating swap partitions:
     <command>mkswap</command>.  Again it’s recommended to assign a
     label to the swap partition: <option>-L
-    <replaceable>label</replaceable></option>.</para></listitem>
-
-    <listitem><para>For creating LVM volumes, the LVM commands, e.g.,
+    <replaceable>label</replaceable></option>. For example:
 
 <screen>
-# pvcreate /dev/sda1 /dev/sdb1
-# vgcreate MyVolGroup /dev/sda1 /dev/sdb1
-# lvcreate --size 2G --name bigdisk MyVolGroup
-# lvcreate --size 1G --name smalldisk MyVolGroup</screen>
+# mkswap -L swap /dev/sda2</screen>
 
     </para></listitem>
 
+    <listitem>
+    <variablelist>
+      <varlistentry><term>UEFI systems</term>
+      <listitem><para>For creating boot partitions:
+    <command>mkfs.fat</command>.  Again it’s recommended to assign a
+    label to the boot partition: <option>-n
+    <replaceable>label</replaceable></option>. For example:
+
+<screen>
+# mkfs.fat -F 32 -n boot /dev/sda3</screen>
+
+    </para></listitem></varlistentry></variablelist></listitem>
+
+    <listitem><para>For creating LVM volumes, the LVM commands, e.g.,
+    <command>pvcreate</command>, <command>vgcreate</command>, and
+    <command>lvcreate</command>.</para></listitem>
+
     <listitem><para>For creating software RAID devices, use
     <command>mdadm</command>.</para></listitem>
 
@@ -95,11 +143,28 @@
 
   </para></listitem>
 
+  <listitem>
+    <variablelist>
+      <varlistentry><term>UEFI systems</term>
+      <listitem><para>Mount the boot file system on <filename>/mnt/boot</filename>, e.g.
+
+<screen>
+# mkdir -p /mnt/boot
+# mount /dev/disk/by-label/boot /mnt/boot
+</screen>
+
+  </para></listitem></varlistentry></variablelist></listitem>
+
   <listitem><para>If your machine has a limited amount of memory, you
   may want to activate swap devices now (<command>swapon
   <replaceable>device</replaceable></command>).  The installer (or
   rather, the build actions that it may spawn) may need quite a bit of
-  RAM, depending on your configuration.</para></listitem>
+  RAM, depending on your configuration.
+
+<screen>
+# swapon /dev/sda2</screen>
+
+  </para></listitem>
 
   <listitem>
 
@@ -135,10 +200,25 @@
     install Emacs by running <literal>nix-env -i
     emacs</literal>.</para>
 
-    <para>You <emphasis>must</emphasis> set the option
+    <variablelist>
+
+      <varlistentry><term>BIOS systems</term>
+      <listitem><para>You <emphasis>must</emphasis> set the option
     <option>boot.loader.grub.device</option> to specify on which disk
     the GRUB boot loader is to be installed.  Without it, NixOS cannot
-    boot.</para>
+      boot.</para></listitem></varlistentry>
+
+      <varlistentry><term>UEFI systems</term>
+      <listitem><para>You <emphasis>must</emphasis> set the option
+      <option>boot.loader.systemd-boot.enable</option> to <literal>true</literal>.
+      <command>nixos-generate-config</command> should do this automatically for new
+      configurations when booted in
+      UEFI mode.</para>
+      <para>You may want to look at the options starting with
+      <option>boot.loader.efi</option> and <option>boot.loader.systemd-boot</option>
+      as well.</para></listitem></varlistentry>
+
+    </variablelist>
 
     <para>If there are other operating systems running on the machine before
     installing NixOS, the
@@ -190,11 +270,20 @@ Enter new UNIX password: ***
 Retype new UNIX password: ***
 </screen>
 
+    <note>
+      <para>
+        To prevent the password prompt, set <code>users.mutableUsers = false;</code> in
+        <filename>configuration.nix</filename>, which allows unattended installation
+        necessary in automation.
+      </para>
+    </note>
+
     </para>
 
   </listitem>
 
-  <listitem><para>If everything went well:
+  <listitem>
+    <para>If everything went well:
 
 <screen>
 # reboot</screen>
@@ -247,10 +336,35 @@ drive (here <filename>/dev/sda</filename>).  <xref linkend="ex-config"
 <example xml:id='ex-install-sequence'><title>Commands for Installing NixOS on <filename>/dev/sda</filename></title>
 <screen>
 # fdisk /dev/sda # <lineannotation>(or whatever device you want to install on)</lineannotation>
+-- for UEFI systems only
+> n      # <lineannotation>(create a new partition for /boot)</lineannotation>
+> 3      # <lineannotation>(make it a partition number 3)</lineannotation>
+>        # <lineannotation>(press enter to accept the default)</lineannotation>
+> +512M  # <lineannotation>(the size of the UEFI boot partition)</lineannotation>
+> t      # <lineannotation>(change the partition type ...)</lineannotation>
+> 3      # <lineannotation>(... of the boot partition ...)</lineannotation>
+> 1      # <lineannotation>(... to 'UEFI System')</lineannotation>
+-- for BIOS or UEFI systems
+> n      # <lineannotation>(create a new partition for /swap)</lineannotation>
+> 2      # <lineannotation>(make it a partition number 2)</lineannotation>
+>        # <lineannotation>(press enter to accept the default)</lineannotation>
+> +8G    # <lineannotation>(the size of the swap partition)</lineannotation>
+> n      # <lineannotation>(create a new partition for /)</lineannotation>
+> 1      # <lineannotation>(make it a partition number 1)</lineannotation>
+>        # <lineannotation>(press enter to accept the default)</lineannotation>
+>        # <lineannotation>(press enter to accept the default and use the rest of the remaining space)</lineannotation>
+> a      # <lineannotation>(make the partition bootable)</lineannotation>
+> x      # <lineannotation>(enter expert mode)</lineannotation>
+> f      # <lineannotation>(fix up the partition ordering)</lineannotation>
+> r      # <lineannotation>(exit expert mode)</lineannotation>
+> w      # <lineannotation>(write the partition table to disk and exit)</lineannotation>
 # mkfs.ext4 -L nixos /dev/sda1
 # mkswap -L swap /dev/sda2
 # swapon /dev/sda2
+# mkfs.fat -F 32 -n boot /dev/sda3        # <lineannotation>(for UEFI systems only)</lineannotation>
 # mount /dev/disk/by-label/nixos /mnt
+# mkdir -p /mnt/boot                      # <lineannotation>(for UEFI systems only)</lineannotation>
+# mount /dev/disk/by-label/boot /mnt/boot # <lineannotation>(for UEFI systems only)</lineannotation>
 # nixos-generate-config --root /mnt
 # nano /mnt/etc/nixos/configuration.nix
 # nixos-install
@@ -267,7 +381,8 @@ drive (here <filename>/dev/sda</filename>).  <xref linkend="ex-config"
       ./hardware-configuration.nix
     ];
 
-  boot.loader.grub.device = "/dev/sda";
+  boot.loader.grub.device = "/dev/sda";   # <lineannotation>(for BIOS systems only)</lineannotation>
+  boot.loader.systemd-boot.enable = true; # <lineannotation>(for UEFI systems only)</lineannotation>
 
   # Note: setting fileSystems is generally not
   # necessary, since nixos-generate-config figures them out
@@ -279,9 +394,9 @@ drive (here <filename>/dev/sda</filename>).  <xref linkend="ex-config"
 }</screen>
 </example>
 
-<xi:include href="installing-uefi.xml" />
 <xi:include href="installing-usb.xml" />
 <xi:include href="installing-pxe.xml" />
 <xi:include href="installing-virtualbox-guest.xml" />
+<xi:include href="installing-from-other-distro.xml" />
 
 </chapter>
diff --git a/nixos/doc/manual/installation/obtaining.xml b/nixos/doc/manual/installation/obtaining.xml
index 20a4838be880..9b2b474c60ce 100644
--- a/nixos/doc/manual/installation/obtaining.xml
+++ b/nixos/doc/manual/installation/obtaining.xml
@@ -12,11 +12,10 @@ download page</link>.  There are a number of installation options.  If
 you happen to have an optical drive and a spare CD, burning the
 image to CD and booting from that is probably the easiest option.
 Most people will need to prepare a USB stick to boot from.
-Unetbootin is recommended and the process is described in brief below.
-Note that systems which use UEFI require some additional manual steps.
-If you run into difficulty a number of alternative methods are presented
-in the <link
-xlink:href="https://nixos.org/wiki/Installing_NixOS_from_a_USB_stick">NixOS
+<xref linkend="sec-booting-from-usb"/> describes the preferred method
+to prepare a USB stick.
+A number of alternative methods are presented in the <link
+xlink:href="https://nixos.wiki/wiki/NixOS_Installation_Guide#Making_the_installation_media">NixOS
 Wiki</link>.</para>
 
 <para>As an alternative to installing NixOS yourself, you can get a
diff --git a/nixos/doc/manual/man-nixos-build-vms.xml b/nixos/doc/manual/man-nixos-build-vms.xml
index 878ebee05273..f4b59a7c6d4b 100644
--- a/nixos/doc/manual/man-nixos-build-vms.xml
+++ b/nixos/doc/manual/man-nixos-build-vms.xml
@@ -40,7 +40,7 @@ points to the generated virtual network.
   test1 = {pkgs, config, ...}:
     {
       services.openssh.enable = true;
-      nixpkgs.system = "i686-linux";
+      nixpkgs.localSystem.system = "i686-linux";
       deployment.targetHost = "test1.example.net";
 
       # Other NixOS options
@@ -51,7 +51,7 @@ points to the generated virtual network.
       services.openssh.enable = true;
       services.httpd.enable = true;
       environment.systemPackages = [ pkgs.lynx ];
-      nixpkgs.system = "x86_64-linux";
+      nixpkgs.localSystem.system = "x86_64-linux";
       deployment.targetHost = "test2.example.net";
 
       # Other NixOS options
@@ -66,7 +66,7 @@ In each NixOS configuration, two attributes have a special meaning.
 The <varname>deployment.targetHost</varname> specifies the address
 (domain name or IP address)
 of the system which is used by <command>ssh</command> to perform
-remote deployment operations. The <varname>nixpkgs.system</varname>
+remote deployment operations. The <varname>nixpkgs.localSystem.system</varname>
 attribute can be used to specify an architecture for the target machine,
 such as <varname>i686-linux</varname> which builds a 32-bit NixOS
 configuration. Omitting this property will build the configuration
diff --git a/nixos/doc/manual/man-nixos-enter.xml b/nixos/doc/manual/man-nixos-enter.xml
new file mode 100644
index 000000000000..a2fbe07961db
--- /dev/null
+++ b/nixos/doc/manual/man-nixos-enter.xml
@@ -0,0 +1,119 @@
+<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-enter</command></refentrytitle>
+  <manvolnum>8</manvolnum>
+  <refmiscinfo class="source">NixOS</refmiscinfo>
+  <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
+</refmeta>
+
+<refnamediv>
+  <refname><command>nixos-enter</command></refname>
+  <refpurpose>run a command in a NixOS chroot environment</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+  <cmdsynopsis>
+    <command>nixos-enter</command>
+    <arg>
+      <arg choice='plain'><option>--root</option></arg>
+      <replaceable>root</replaceable>
+    </arg>
+    <arg>
+      <arg choice='plain'><option>--system</option></arg>
+      <replaceable>system</replaceable>
+    </arg>
+    <arg>
+      <arg choice='plain'><option>-c</option></arg>
+      <replaceable>shell-command</replaceable>
+    </arg>
+    <arg>
+      <arg choice='plain'><option>--help</option></arg>
+    </arg>
+    <arg>
+      <arg choice='plain'><option>--</option></arg>
+      <replaceable>arguments</replaceable>
+    </arg>
+  </cmdsynopsis>
+</refsynopsisdiv>
+
+
+<refsection><title>Description</title>
+
+<para>This command runs a command in a NixOS chroot environment, that
+is, in a filesystem hierarchy previously prepared using
+<command>nixos-install</command>.</para>
+
+</refsection>
+
+<refsection><title>Options</title>
+
+<para>This command accepts the following options:</para>
+
+<variablelist>
+
+  <varlistentry>
+    <term><option>--root</option></term>
+    <listitem>
+      <para>The path to the NixOS system you want to enter. It defaults to <filename>/mnt</filename>.</para>
+    </listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><option>--system</option></term>
+    <listitem>
+      <para>The NixOS system configuration to use. It defaults to
+      <filename>/nix/var/nix/profiles/system</filename>. You can enter
+      a previous NixOS configuration by specifying a path such as
+      <filename>/nix/var/nix/profiles/system-106-link</filename>.</para>
+    </listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><option>--command</option></term>
+    <term><option>-c</option></term>
+    <listitem>
+      <para>The bash command to execute.</para>
+    </listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><option>--</option></term>
+
+    <listitem><para>Interpret the remaining arguments as the program
+    name and arguments to be invoked. The program is not executed in a
+    shell.</para></listitem>
+
+  </varlistentry>
+
+</variablelist>
+
+</refsection>
+
+
+<refsection><title>Examples</title>
+
+<para>Start an interactive shell in the NixOS installation in
+<filename>/mnt</filename>:</para>
+
+<screen>
+# nixos-enter /mnt
+</screen>
+
+<para>Run a shell command:</para>
+
+<screen>
+# nixos-enter -c 'ls -l /; cat /proc/mounts'
+</screen>
+
+<para>Run a non-shell command:</para>
+
+<screen>
+# nixos-enter -- cat /proc/mounts
+</screen>
+
+</refsection>
+
+</refentry>
diff --git a/nixos/doc/manual/man-nixos-install.xml b/nixos/doc/manual/man-nixos-install.xml
index 15c603256ca7..c9887146989b 100644
--- a/nixos/doc/manual/man-nixos-install.xml
+++ b/nixos/doc/manual/man-nixos-install.xml
@@ -26,8 +26,8 @@
       <replaceable>root</replaceable>
     </arg>
     <arg>
-      <arg choice='plain'><option>--closure</option></arg>
-      <replaceable>closure</replaceable>
+      <arg choice='plain'><option>--system</option></arg>
+      <replaceable>path</replaceable>
     </arg>
     <arg>
       <arg choice='plain'><option>--no-channel-copy</option></arg>
@@ -118,7 +118,7 @@ it.</para>
   </varlistentry>
 
   <varlistentry>
-    <term><option>--closure</option></term>
+    <term><option>--system</option></term>
     <listitem>
       <para>If this option is provided, <command>nixos-install</command> will install the specified closure
       rather than attempt to build one from <filename>/mnt/etc/nixos/configuration.nix</filename>.</para>
diff --git a/nixos/doc/manual/man-pages.xml b/nixos/doc/manual/man-pages.xml
index e945e0e62639..80a8458fbfec 100644
--- a/nixos/doc/manual/man-pages.xml
+++ b/nixos/doc/manual/man-pages.xml
@@ -15,7 +15,7 @@
     </author>
 
     <copyright>
-      <year>2007-2015</year>
+      <year>2007-2018</year>
       <holder>Eelco Dolstra</holder>
     </copyright>
 
@@ -25,6 +25,7 @@
   <xi:include href="man-nixos-build-vms.xml" />
   <xi:include href="man-nixos-generate-config.xml" />
   <xi:include href="man-nixos-install.xml" />
+  <xi:include href="man-nixos-enter.xml" />
   <xi:include href="man-nixos-option.xml" />
   <xi:include href="man-nixos-rebuild.xml" />
   <xi:include href="man-nixos-version.xml" />
diff --git a/nixos/doc/manual/options-to-docbook.xsl b/nixos/doc/manual/options-to-docbook.xsl
index 5387546b5982..7b45b233ab2a 100644
--- a/nixos/doc/manual/options-to-docbook.xsl
+++ b/nixos/doc/manual/options-to-docbook.xsl
@@ -70,6 +70,15 @@
                 </para>
               </xsl:if>
 
+              <xsl:if test="attr[@name = 'relatedPackages']">
+                <para>
+                  <emphasis>Related packages:</emphasis>
+                  <xsl:text> </xsl:text>
+                  <xsl:value-of disable-output-escaping="yes"
+                                select="attr[@name = 'relatedPackages']/string/@value" />
+                </para>
+              </xsl:if>
+
               <xsl:if test="count(attr[@name = 'declarations']/list/*) != 0">
                 <para>
                   <emphasis>Declared by:</emphasis>
diff --git a/nixos/doc/manual/release-notes/release-notes.xml b/nixos/doc/manual/release-notes/release-notes.xml
index 5ed56bde6659..b7f9fab44f3b 100644
--- a/nixos/doc/manual/release-notes/release-notes.xml
+++ b/nixos/doc/manual/release-notes/release-notes.xml
@@ -9,6 +9,7 @@
 <para>This section lists the release notes for each stable version of NixOS
 and current unstable revision.</para>
 
+<xi:include href="rl-1809.xml" />
 <xi:include href="rl-1803.xml" />
 <xi:include href="rl-1709.xml" />
 <xi:include href="rl-1703.xml" />
diff --git a/nixos/doc/manual/release-notes/rl-1404.xml b/nixos/doc/manual/release-notes/rl-1404.xml
index 36f67ed88b0b..137caf14cba2 100644
--- a/nixos/doc/manual/release-notes/rl-1404.xml
+++ b/nixos/doc/manual/release-notes/rl-1404.xml
@@ -13,7 +13,7 @@ the following highlights:
 <itemizedlist>
 
   <listitem><para>Installation on UEFI systems is now supported.  See
-  <xref linkend="sec-uefi-installation"/> for
+  <xref linkend="sec-installation"/> for
   details.</para></listitem>
 
   <listitem><para>Systemd has been updated to version 212, which has
diff --git a/nixos/doc/manual/release-notes/rl-1803.xml b/nixos/doc/manual/release-notes/rl-1803.xml
index c1fe692ceecb..9221c2951ed2 100644
--- a/nixos/doc/manual/release-notes/rl-1803.xml
+++ b/nixos/doc/manual/release-notes/rl-1803.xml
@@ -4,7 +4,7 @@
          version="5.0"
          xml:id="sec-release-18.03">
 
-<title>Release 18.03 (“Impala”, 2018/03/??)</title>
+<title>Release 18.03 (“Impala”, 2018/04/04)</title>
 
 <section xmlns="http://docbook.org/ns/docbook"
          xmlns:xlink="http://www.w3.org/1999/xlink"
@@ -18,10 +18,63 @@
 has the following highlights: </para>
 
 <itemizedlist>
+
+  <listitem>
+    <para>
+      End of support is planned for end of October 2018, handing over to 18.09.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      Platform support: x86_64-linux and x86_64-darwin since release time (the latter isn't NixOS, really).
+      Binaries for aarch64-linux are available, but no channel exists yet, as it's waiting for some test fixes, etc.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      Nix now defaults to 2.0; see its
+      <link xlink:href="https://nixos.org/nix/manual/#ssec-relnotes-2.0">release notes</link>.
+    </para>
+  </listitem>
+
   <listitem>
     <para>
+      Core version changes: linux: 4.9 -> 4.14, glibc: 2.25 -> 2.26, gcc: 6 -> 7, systemd: 234 -> 237.
     </para>
   </listitem>
+
+  <listitem>
+    <para>
+      Desktop version changes: gnome: 3.24 -> 3.26, (KDE) plasma-desktop: 5.10 -> 5.12.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      MariaDB 10.2, updated from 10.1, is now the default MySQL implementation. While upgrading a few changes
+      have been made to the infrastructure involved:
+      <itemizedlist>
+        <listitem>
+          <para>
+            <literal>libmysql</literal> has been deprecated, please use <literal>mysql.connector-c</literal>
+            instead, a compatibility passthru has been added to the MySQL packages.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The <literal>mysql57</literal> package has a new <literal>static</literal> output containing
+            the static libraries including <literal>libmysqld.a</literal>
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>PHP now defaults to PHP 7.2, updated from 7.1.</para>
+  </listitem>
 </itemizedlist>
 
 </section>
@@ -36,9 +89,66 @@ has the following highlights: </para>
 <para>The following new services were added since the last release:</para>
 
 <itemizedlist>
-  <listitem>
-    <para></para>
-  </listitem>
+  <listitem><para><literal>./config/krb5/default.nix</literal></para></listitem>
+  <listitem><para><literal>./hardware/digitalbitbox.nix</literal></para></listitem>
+  <listitem><para><literal>./misc/label.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/ccache.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/criu.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/digitalbitbox/default.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/less.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/npm.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/plotinus.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/rootston.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/systemtap.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/sway.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/udevil.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/way-cooler.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/yabar.nix</literal></para></listitem>
+  <listitem><para><literal>./programs/zsh/zsh-autoenv.nix</literal></para></listitem>
+  <listitem><para><literal>./services/backup/borgbackup.nix</literal></para></listitem>
+  <listitem><para><literal>./services/backup/crashplan-small-business.nix</literal></para></listitem>
+  <listitem><para><literal>./services/desktops/dleyna-renderer.nix</literal></para></listitem>
+  <listitem><para><literal>./services/desktops/dleyna-server.nix</literal></para></listitem>
+  <listitem><para><literal>./services/desktops/pipewire.nix</literal></para></listitem>
+  <listitem><para><literal>./services/desktops/gnome3/chrome-gnome-shell.nix</literal></para></listitem>
+  <listitem><para><literal>./services/desktops/gnome3/tracker-miners.nix</literal></para></listitem>
+  <listitem><para><literal>./services/hardware/fwupd.nix</literal></para></listitem>
+  <listitem><para><literal>./services/hardware/interception-tools.nix</literal></para></listitem>
+  <listitem><para><literal>./services/hardware/u2f.nix</literal></para></listitem>
+  <listitem><para><literal>./services/hardware/usbmuxd.nix</literal></para></listitem>
+  <listitem><para><literal>./services/mail/clamsmtp.nix</literal></para></listitem>
+  <listitem><para><literal>./services/mail/dkimproxy-out.nix</literal></para></listitem>
+  <listitem><para><literal>./services/mail/pfix-srsd.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/gitea.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/home-assistant.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/ihaskell.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/logkeys.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/novacomd.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/osrm.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/plexpy.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/pykms.nix</literal></para></listitem>
+  <listitem><para><literal>./services/misc/tzupdate.nix</literal></para></listitem>
+  <listitem><para><literal>./services/monitoring/fusion-inventory.nix</literal></para></listitem>
+  <listitem><para><literal>./services/monitoring/prometheus/exporters.nix</literal></para></listitem>
+  <listitem><para><literal>./services/network-filesystems/beegfs.nix</literal></para></listitem>
+  <listitem><para><literal>./services/network-filesystems/davfs2.nix</literal></para></listitem>
+  <listitem><para><literal>./services/network-filesystems/openafs/client.nix</literal></para></listitem>
+  <listitem><para><literal>./services/network-filesystems/openafs/server.nix</literal></para></listitem>
+  <listitem><para><literal>./services/network-filesystems/ceph.nix</literal></para></listitem>
+  <listitem><para><literal>./services/networking/aria2.nix</literal></para></listitem>
+  <listitem><para><literal>./services/networking/monero.nix</literal></para></listitem>
+  <listitem><para><literal>./services/networking/nghttpx/default.nix</literal></para></listitem>
+  <listitem><para><literal>./services/networking/nixops-dns.nix</literal></para></listitem>
+  <listitem><para><literal>./services/networking/rxe.nix</literal></para></listitem>
+  <listitem><para><literal>./services/networking/stunnel.nix</literal></para></listitem>
+  <listitem><para><literal>./services/web-apps/matomo.nix</literal></para></listitem>
+  <listitem><para><literal>./services/web-apps/restya-board.nix</literal></para></listitem>
+  <listitem><para><literal>./services/web-servers/mighttpd2.nix</literal></para></listitem>
+  <listitem><para><literal>./services/x11/fractalart.nix</literal></para></listitem>
+  <listitem><para><literal>./system/boot/binfmt.nix</literal></para></listitem>
+  <listitem><para><literal>./system/boot/grow-partition.nix</literal></para></listitem>
+  <listitem><para><literal>./tasks/filesystems/ecryptfs.nix</literal></para></listitem>
+  <listitem><para><literal>./virtualisation/hyperv-guest.nix</literal></para></listitem>
 </itemizedlist>
 
 </section>
@@ -56,6 +166,11 @@ following incompatible changes:</para>
 <itemizedlist>
   <listitem>
     <para>
+      <literal>sound.enable</literal> now defaults to false.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
       Dollar signs in options under <option>services.postfix</option> are
       passed verbatim to Postfix, which will interpret them as the beginning of
       a parameter expression. This was already true for string-valued options
@@ -72,6 +187,243 @@ following incompatible changes:</para>
       <option>services.pgmanage</option>.
     </para>
   </listitem>
+  <listitem>
+    <para>
+      Package attributes starting with a digit have been prefixed with an
+      underscore sign. This is to avoid quoting in the configuration and
+      other issues with command-line tools like <literal>nix-env</literal>.
+      The change affects the following packages:
+      <itemizedlist>
+        <listitem>
+          <para><literal>2048-in-terminal</literal> → <literal>_2048-in-terminal</literal></para>
+        </listitem>
+        <listitem>
+          <para><literal>90secondportraits</literal> → <literal>_90secondportraits</literal></para>
+        </listitem>
+        <listitem>
+          <para><literal>2bwm</literal> → <literal>_2bwm</literal></para>
+        </listitem>
+        <listitem>
+          <para><literal>389-ds-base</literal> → <literal>_389-ds-base</literal></para>
+        </listitem>
+      </itemizedlist>
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <emphasis role="strong">
+        The OpenSSH service no longer enables support for DSA keys by default,
+        which could cause a system lock out. Update your keys or, unfavorably,
+        re-enable DSA support manually.
+      </emphasis>
+    </para>
+
+    <para>
+      DSA support was
+      <link xlink:href="https://www.openssh.com/legacy.html">deprecated in OpenSSH 7.0</link>,
+      due to it being too weak. To re-enable support, add
+      <literal>PubkeyAcceptedKeyTypes +ssh-dss</literal> to the end of your
+      <option>services.openssh.extraConfig</option>.
+    </para>
+
+    <para>
+      After updating the keys to be stronger, anyone still on a pre-17.03
+      version is safe to jump to 17.03, as vetted
+      <link xlink:href="https://search.nix.gsc.io/?q=stateVersion">here</link>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The <literal>openssh</literal> package
+      now includes Kerberos support by default;
+      the <literal>openssh_with_kerberos</literal> package
+      is now a deprecated alias.
+      If you do not want Kerberos support,
+      you can do <literal>openssh.override { withKerberos = false; }</literal>.
+      Note, this also applies to the <literal>openssh_hpn</literal> package.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>cc-wrapper</literal> has been split in two; there is now also a <literal>bintools-wrapper</literal>.
+      The most commonly used files in <filename>nix-support</filename> are now split between the two wrappers.
+      Some commonly used ones, like <filename>nix-support/dynamic-linker</filename>, are duplicated for backwards compatability, even though they rightly belong only in <literal>bintools-wrapper</literal>.
+      Other more obscure ones are just moved.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The propagation logic has been changed.
+      The new logic, along with new types of dependencies that go with, is thoroughly documented in the "Specifying dependencies" section of the "Standard Environment" chapter of the nixpkgs manual.
+      <!-- That's <xref linkend="ssec-stdenv-attributes"> were we to merge the manuals. -->
+      The old logic isn't but is easy to describe: dependencies were propagated as the same type of dependency no matter what.
+      In practice, that means that many <function>propagatedNativeBuildInputs</function> should instead be  <function>propagatedBuildInputs</function>.
+      Thankfully, that was and is the least used type of dependency.
+      Also, it means that some <function>propagatedBuildInputs</function> should instead be <function>depsTargetTargetPropagated</function>.
+      Other types dependencies should be unaffected.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>lib.addPassthru drv passthru</literal> is removed.  Use <literal>lib.extendDerivation true passthru drv</literal> instead.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The <literal>memcached</literal> service no longer accept dynamic socket
+      paths via <option>services.memcached.socket</option>. Unix sockets can be
+      still enabled by <option>services.memcached.enableUnixSocket</option> and
+      will be accessible at <literal>/run/memcached/memcached.sock</literal>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The <varname>hardware.amdHybridGraphics.disable</varname> option was removed for lack of a maintainer. If you still need this module, you may wish to include a copy of it from an older version of nixos in your imports.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The merging of config options for <varname>services.postfix.config</varname>
+      was buggy. Previously, if other options in the Postfix module like
+      <varname>services.postfix.useSrs</varname> were set and the user set config
+      options that were also set by such options, the resulting config wouldn't
+      include all options that were needed. They are now merged correctly. If
+      config options need to be overridden, <literal>lib.mkForce</literal> or
+      <literal>lib.mkOverride</literal> can be used.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The following changes apply if the <literal>stateVersion</literal> is changed to 18.03 or higher.
+      For <literal>stateVersion = "17.09"</literal> or lower the old behavior is preserved.
+    </para>
+    <itemizedlist>
+      <listitem>
+        <para>
+          <literal>matrix-synapse</literal> uses postgresql by default instead of sqlite.
+          Migration instructions can be found <link xlink:href="https://github.com/matrix-org/synapse/blob/master/docs/postgres.rst#porting-from-sqlite"> here </link>.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </listitem>
+  <listitem>
+    <para>
+      The <literal>jid</literal> package has been removed, due to maintenance
+      overhead of a go package having non-versioned dependencies.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      When using <option>services.xserver.libinput</option> (enabled by default in GNOME),
+      it now handles all input devices, not just touchpads. As a result, you might need to
+      re-evaluate any custom Xorg configuration. In particular,
+      <literal>Option "XkbRules" "base"</literal> may result in broken keyboard layout.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The <literal>attic</literal> package was removed. A maintained fork called
+      <link xlink:href="https://www.borgbackup.org/">Borg</link> should be used instead.
+      Migration instructions can be found
+      <link xlink:href="http://borgbackup.readthedocs.io/en/stable/usage/upgrade.html#attic-and-borg-0-xx-to-borg-1-x">here</link>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The Piwik analytics software was renamed to Matomo:
+      <itemizedlist>
+        <listitem>
+          <para>The package <literal>pkgs.piwik</literal> was renamed to <literal>pkgs.matomo</literal>.</para>
+        </listitem>
+        <listitem>
+          <para>The service <literal>services.piwik</literal> was renamed to <literal>services.matomo</literal>.</para>
+        </listitem>
+        <listitem>
+          <para>
+            The data directory <filename>/var/lib/piwik</filename> was renamed to <filename>/var/lib/matomo</filename>.
+            All files will be moved automatically on first startup, but you might need to adjust your backup scripts.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The default <option>serverName</option> for the nginx configuration changed from
+            <literal>piwik.${config.networking.hostName}</literal> to
+            <literal>matomo.${config.networking.hostName}.${config.networking.domain}</literal>
+            if <option>config.networking.domain</option> is set,
+            <literal>matomo.${config.networking.hostName}</literal> if it is not set.
+            If you change your <option>serverName</option>, remember you'll need to update the
+            <literal>trustedHosts[]</literal> array in <filename>/var/lib/matomo/config/config.ini.php</filename>
+            as well.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The <literal>piwik</literal> user was renamed to <literal>matomo</literal>.
+            The service will adjust ownership automatically for files in the data directory.
+            If you use unix socket authentication, remember to give the new <literal>matomo</literal> user
+            access to the database and to change the <literal>username</literal> to <literal>matomo</literal>
+            in the <literal>[database]</literal> section of <filename>/var/lib/matomo/config/config.ini.php</filename>.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            If you named your database `piwik`, you might want to rename it to `matomo` to keep things clean,
+            but this is neither enforced nor required.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>nodejs-4_x</literal> is end-of-life.
+      <literal>nodejs-4_x</literal>, <literal>nodejs-slim-4_x</literal> and <literal>nodePackages_4_x</literal> are removed.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The <literal>pump.io</literal> NixOS module was removed.
+      It is now maintained as an
+      <link xlink:href="https://github.com/rvl/pump.io-nixos">external module</link>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The Prosody XMPP server has received a major update. The following modules were renamed:
+      <itemizedlist>
+        <listitem>
+          <para>
+            <option>services.prosody.modules.httpserver</option> is now <option>services.prosody.modules.http_files</option>
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <option>services.prosody.modules.console</option> is now <option>services.prosody.modules.admin_telnet</option>
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+
+    <para>
+      Many new modules are now core modules, most notably <option>services.prosody.modules.carbons</option>
+      and <option>services.prosody.modules.mam</option>.
+    </para>
+
+    <para>
+      The better-performing <literal>libevent</literal> backend is now enabled by default.
+    </para>
+
+    <para>
+      <literal>withCommunityModules</literal> now passes through the modules to <option>services.prosody.extraModules</option>.
+      Use <literal>withOnlyInstalledCommunityModules</literal> for modules that should not be enabled directly, e.g <literal>lib_ldap</literal>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      All prometheus exporter modules are now defined as submodules.
+      The exporters are configured using <literal>services.prometheus.exporters</literal>.
+    </para>
+  </listitem>
 </itemizedlist>
 
 </section>
@@ -102,10 +454,106 @@ following incompatible changes:</para>
   </listitem>
   <listitem>
     <para>
-      The option <option>services.xserver.desktopManager.default</option> is now <literal>none</literal> by default.
-      An assertion failure is thrown if WM's and DM's default are <literal>none</literal>.
-      To explicitly run a plain X session without and DM or WM, the newly introduced option <option>services.xserver.plainX</option>
-      must be set to true.
+      In the module <option>networking.interfaces.&lt;name&gt;</option> the
+      following options have been removed:
+      <itemizedlist>
+        <listitem>
+          <para><option>ipAddress</option></para>
+        </listitem>
+        <listitem>
+          <para><option>ipv6Address</option></para>
+        </listitem>
+        <listitem>
+          <para><option>prefixLength</option></para>
+        </listitem>
+        <listitem>
+          <para><option>ipv6PrefixLength</option></para>
+        </listitem>
+        <listitem>
+          <para><option>subnetMask</option></para>
+        </listitem>
+      </itemizedlist>
+      To assign static addresses to an interface the options
+      <option>ipv4.addresses</option> and <option>ipv6.addresses</option>
+      should be used instead.
+      The options <option>ip4</option> and <option>ip6</option> have been
+      renamed to <option>ipv4.addresses</option> <option>ipv6.addresses</option>
+      respectively.
+      The new options <option>ipv4.routes</option> and <option>ipv6.routes</option>
+      have been added to set up static routing.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The option <option>services.logstash.listenAddress</option> is now <literal>127.0.0.1</literal> by default.
+      Previously the default behaviour was to listen on all interfaces.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>services.btrfs.autoScrub</literal> has been added, to
+      periodically check btrfs filesystems for data corruption.
+      If there's a correct copy available, it will automatically repair
+      corrupted blocks.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>displayManager.lightdm.greeters.gtk.clock-format.</literal>
+      has been added, the clock format string (as expected by
+      strftime, e.g. <literal>%H:%M</literal>) to use with the lightdm
+      gtk greeter panel.
+    </para>
+    <para>
+      If set to null the default clock format is used.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>displayManager.lightdm.greeters.gtk.indicators</literal>
+      has been added, a list of allowed indicator modules to use with
+      the lightdm gtk greeter panel.
+    </para>
+    <para>
+      Built-in indicators include <literal>~a11y</literal>,
+      <literal>~language</literal>, <literal>~session</literal>,
+      <literal>~power</literal>, <literal>~clock</literal>,
+      <literal>~host</literal>, <literal>~spacer</literal>. Unity
+      indicators can be represented by short name
+      (e.g. <literal>sound</literal>, <literal>power</literal>),
+      service file name, or absolute path.
+    </para>
+    <para>
+      If set to <literal>null</literal> the default indicators are
+      used.
+    </para>
+    <para>
+      In order to have the previous default configuration add
+<programlisting>
+  services.xserver.displayManager.lightdm.greeters.gtk.indicators = [
+    "~host" "~spacer"
+    "~clock" "~spacer"
+    "~session"
+    "~language"
+    "~a11y"
+    "~power"
+  ];
+</programlisting>
+      to your <literal>configuration.nix</literal>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The NixOS test driver supports user services declared by <literal>systemd.user.services</literal>.
+      The methods <literal>waitForUnit</literal>, <literal>getUnitInfo</literal>, <literal>startJob</literal>
+      and <literal>stopJob</literal> provide an optional <literal>$user</literal> argument for that purpose.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      Enabling bash completion on NixOS, <literal>programs.bash.enableCompletion</literal>, will now also enable
+      completion for the Nix command line tools by installing the
+      <link xlink:href="https://github.com/hedning/nix-bash-completions">nix-bash-completions</link> package.
     </para>
   </listitem>
 </itemizedlist>
diff --git a/nixos/doc/manual/release-notes/rl-1809.xml b/nixos/doc/manual/release-notes/rl-1809.xml
new file mode 100644
index 000000000000..61f9ec8ba995
--- /dev/null
+++ b/nixos/doc/manual/release-notes/rl-1809.xml
@@ -0,0 +1,85 @@
+<section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-18.09">
+
+<title>Release 18.09 (“Jellyfish”, 2018/09/??)</title>
+
+<section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-18.09-highlights">
+
+<title>Highlights</title>
+
+<para>In addition to numerous new and upgraded packages, this release
+has the following highlights: </para>
+
+<itemizedlist>
+  <listitem>
+    <para>
+      TODO
+    </para>
+  </listitem>
+
+</itemizedlist>
+
+</section>
+<section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-18.09-new-services">
+
+<title>New Services</title>
+
+<para>The following new services were added since the last release:</para>
+
+<itemizedlist>
+  <listitem>
+    <para></para>
+  </listitem>
+</itemizedlist>
+
+</section>
+<section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-18.09-incompatibilities">
+
+<title>Backward Incompatibilities</title>
+
+<para>When upgrading from a previous release, please be aware of the
+following incompatible changes:</para>
+
+<itemizedlist>
+  <listitem>
+    <para>
+      The <literal>clementine</literal> package points now to the free derivation.
+      <literal>clementineFree</literal> is removed now and <literal>clementineUnfree</literal>
+      points to the package which is bundled with the unfree <literal>libspotify</literal> package.
+    </para>
+  </listitem>
+</itemizedlist>
+
+</section>
+<section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-18.09-notable-changes">
+
+<title>Other Notable Changes</title>
+
+<itemizedlist>
+  <listitem>
+    <para>
+    </para>
+  </listitem>
+</itemizedlist>
+
+</section>
+</section>
diff --git a/nixos/doc/manual/style.css b/nixos/doc/manual/style.css
deleted file mode 100644
index 3118b37ead1f..000000000000
--- a/nixos/doc/manual/style.css
+++ /dev/null
@@ -1,267 +0,0 @@
-/* Copied from http://bakefile.sourceforge.net/, which appears
-   licensed under the GNU GPL. */
-
-
-/***************************************************************************
-                             Basic headers and text:
- ***************************************************************************/
-
-body
-{
-    font-family: "Nimbus Sans L", sans-serif;
-    background: white;
-    margin: 2em 1em 2em 1em;
-}
-
-h1, h2, h3, h4
-{
-    color: #005aa0;
-}
-
-h1 /* title */
-{
-    font-size: 200%;
-}
-
-h2 /* chapters, appendices, subtitle */
-{
-    font-size: 180%;
-}
-
-/* Extra space between chapters, appendices. */
-div.chapter > div.titlepage h2, div.appendix > div.titlepage h2 
-{ 
-    margin-top: 1.5em;
-}
-
-div.section > div.titlepage h2 /* sections */
-{
-    font-size: 150%;
-    margin-top: 1.5em;
-}
-
-h3 /* subsections */
-{
-    font-size: 125%;
-}
-
-div.simplesect h2
-{
-    font-size: 110%;
-}
-
-div.appendix h3
-{
-    font-size: 150%;
-    margin-top: 1.5em;
-}
-
-div.refnamediv h2, div.refsynopsisdiv h2, div.refsection h2 /* refentry parts */
-{
-    margin-top: 1.4em;
-    font-size: 125%;
-}
-
-div.refsection h3
-{
-    font-size: 110%;
-}
-
-
-/***************************************************************************
-                               Examples:
- ***************************************************************************/
-
-div.example
-{
-    border: 1px solid #b0b0b0;
-    padding: 6px 6px;
-    margin-left: 1.5em;
-    margin-right: 1.5em;
-    background: #f4f4f8;
-    border-radius: 0.4em;
-    box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
-}
-
-div.example p.title
-{
-    margin-top: 0em;
-}
-
-div.example pre
-{
-    box-shadow: none;
-}
-
-
-/***************************************************************************
-                            Screen dumps:
- ***************************************************************************/
-
-pre.screen, pre.programlisting
-{
-    border: 1px solid #b0b0b0;
-    padding: 3px 3px;
-    margin-left: 1.5em;
-    margin-right: 1.5em;
-    color: #600000;
-    background: #f4f4f8;
-    font-family: monospace;
-    border-radius: 0.4em;
-    box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
-}
-
-div.example pre.programlisting
-{
-    border: 0px;
-    padding: 0 0;
-    margin: 0 0 0 0;
-}
-
-
-/***************************************************************************
-                               Notes, warnings etc:
- ***************************************************************************/
-
-.note, .warning
-{
-    border: 1px solid #b0b0b0;
-    padding: 3px 3px;
-    margin-left: 1.5em;
-    margin-right: 1.5em;
-    margin-bottom: 1em;
-    padding: 0.3em 0.3em 0.3em 0.3em;
-    background: #fffff5;
-    border-radius: 0.4em;
-    box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
-}
-
-div.note, div.warning
-{
-    font-style: italic;
-}
-
-div.note h3, div.warning h3
-{
-    color: red;
-    font-size: 100%;
-    padding-right: 0.5em;
-    display: inline;
-}
-
-div.note p, div.warning p
-{
-    margin-bottom: 0em;
-}
-
-div.note h3 + p, div.warning h3 + p
-{
-    display: inline;
-}
-
-div.note h3
-{
-    color: blue;
-    font-size: 100%;
-}
-
-div.navfooter *
-{
-    font-size: 90%;
-}
-
-
-/***************************************************************************
-                        Links colors and highlighting: 
- ***************************************************************************/
-
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-a:link { color: #0048b3; }
-a:visited { color: #002a6a; }
-
-
-/***************************************************************************
-                              Table of contents:
- ***************************************************************************/
-
-div.toc
-{
-    font-size: 90%;
-}
-
-div.toc dl
-{
-    margin-top: 0em;
-    margin-bottom: 0em;
-}
-
-
-/***************************************************************************
-                               Special elements:
- ***************************************************************************/
-
-tt, code
-{
-    color: #400000;
-}
-
-.term
-{
-    font-weight: bold;
-    
-}
-
-div.variablelist dd p, div.glosslist dd p
-{
-    margin-top: 0em;
-}
-
-div.variablelist dd, div.glosslist dd
-{
-    margin-left: 1.5em;
-}
-
-div.glosslist dt
-{
-    font-style: italic;
-}
-
-.varname
-{
-    color: #400000;
-}
-
-span.command strong
-{
-    font-weight: normal;
-    color: #400000;
-}
-
-div.calloutlist table
-{
-    box-shadow: none;
-}
-
-table
-{
-    border-collapse: collapse;
-    box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
-}
-
-table.simplelist
-{
-    text-align: left;
-    color: #005aa0;
-    border: 0;
-    padding: 5px;
-    background: #fffff5;
-    font-weight: normal;
-    font-style: italic;
-    box-shadow: none;
-    margin-bottom: 1em;
-}
-
-div.navheader table, div.navfooter table {
-    box-shadow: none;
-}
diff --git a/nixos/lib/build-vms.nix b/nixos/lib/build-vms.nix
index 1a17a080ba45..e14105f5f011 100644
--- a/nixos/lib/build-vms.nix
+++ b/nixos/lib/build-vms.nix
@@ -3,7 +3,7 @@
 let pkgs = import ../.. { inherit system config; }; in
 
 with pkgs.lib;
-with import ../lib/qemu-flags.nix;
+with import ../lib/qemu-flags.nix { inherit pkgs; };
 
 rec {
 
@@ -51,7 +51,7 @@ rec {
             let
               interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
               interfaces = flip map interfacesNumbered ({ fst, snd }:
-                nameValuePair "eth${toString snd}" { ip4 =
+                nameValuePair "eth${toString snd}" { ipv4.addresses =
                   [ { address = "192.168.${toString fst}.${toString m.snd}";
                       prefixLength = 24;
                   } ];
@@ -64,7 +64,7 @@ rec {
                   networking.interfaces = listToAttrs interfaces;
 
                   networking.primaryIPAddress =
-                    optionalString (interfaces != []) (head (head interfaces).value.ip4).address;
+                    optionalString (interfaces != []) (head (head interfaces).value.ipv4.addresses).address;
 
                   # Put the IP addresses of all VMs in this machine's
                   # /etc/hosts file.  If a machine has multiple
diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix
index 2e7971cca810..97c79487df4c 100644
--- a/nixos/lib/eval-config.nix
+++ b/nixos/lib/eval-config.nix
@@ -26,7 +26,7 @@
 , lib ? import ../../lib
 }:
 
-let extraArgs_ = extraArgs; pkgs_ = pkgs; system_ = system;
+let extraArgs_ = extraArgs; pkgs_ = pkgs;
     extraModules = let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
                    in if e == "" then [] else [(import (builtins.toPath e))];
 in
@@ -36,7 +36,7 @@ let
     _file = ./eval-config.nix;
     key = _file;
     config = {
-      nixpkgs.system = lib.mkDefault system_;
+      nixpkgs.localSystem = lib.mkDefault { inherit system; };
       _module.args.pkgs = lib.mkIf (pkgs_ != null) (lib.mkForce pkgs_);
     };
   };
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index bf25e0cab259..ebfb09db7b7e 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -13,10 +13,16 @@
   # grafted in the file system at path `target'.
 , contents ? []
 
-, # Whether the disk should be partitioned (with a single partition
-  # containing the root filesystem) or contain the root filesystem
-  # directly.
-  partitioned ? true
+, # Type of partition table to use; either "legacy", "efi", or "none".
+  # For "efi" images, the GPT partition table is used and a mandatory ESP
+  #   partition of reasonable size is created in addition to the root partition.
+  #   If `installBootLoader` is true, GRUB will be installed in EFI mode.
+  # For "legacy", the msdos partition table is used and a single large root
+  #   partition is created. If `installBootLoader` is true, GRUB will be
+  #   installed in legacy mode.
+  # For "none", no partition table is created. Enabling `installBootLoader`
+  #   most likely fails as GRUB will probably refuse to install.
+  partitionTableType ? "legacy"
 
   # Whether to invoke switch-to-configuration boot during image creation
 , installBootLoader ? true
@@ -37,11 +43,15 @@
   format ? "raw"
 }:
 
+assert partitionTableType == "legacy" || partitionTableType == "efi" || partitionTableType == "none";
+# We use -E offset=X below, which is only supported by e2fsprogs
+assert partitionTableType != "none" -> fsType == "ext4";
+
 with lib;
 
 let format' = format; in let
 
-  format = if (format' == "qcow2-compressed") then "qcow2" else format';
+  format = if format' == "qcow2-compressed" then "qcow2" else format';
 
   compress = optionalString (format' == "qcow2-compressed") "-c";
 
@@ -51,9 +61,31 @@ let format' = format; in let
     raw   = "img";
   }.${format};
 
+  rootPartition = { # switch-case
+    legacy = "1";
+    efi = "2";
+  }.${partitionTableType};
+
+  partitionDiskScript = { # switch-case
+    legacy = ''
+      parted --script $diskImage -- \
+        mklabel msdos \
+        mkpart primary ext4 1MiB -1
+    '';
+    efi = ''
+      parted --script $diskImage -- \
+        mklabel gpt \
+        mkpart ESP fat32 8MiB 256MiB \
+        set 1 boot on \
+        mkpart primary ext4 256MiB -1
+    '';
+    none = "";
+  }.${partitionTableType};
+
   nixpkgs = cleanSource pkgs.path;
 
-  channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}" {} ''
+  # FIXME: merge with channel.nix / make-channel.nix.
+  channelSources = pkgs.runCommand "nixos-${config.system.nixos.version}" {} ''
     mkdir -p $out
     cp -prd ${nixpkgs} $out/nixos
     chmod -R u+w $out/nixos
@@ -61,16 +93,19 @@ let format' = format; in let
       ln -s . $out/nixos/nixpkgs
     fi
     rm -rf $out/nixos/.git
-    echo -n ${config.system.nixosVersionSuffix} > $out/nixos/.version-suffix
+    echo -n ${config.system.nixos.versionSuffix} > $out/nixos/.version-suffix
   '';
 
-  metaClosure = pkgs.writeText "meta" ''
-    ${config.system.build.toplevel}
-    ${config.nix.package.out}
-    ${channelSources}
-  '';
-
-  prepareImageInputs = with pkgs; [ rsync utillinux parted e2fsprogs lkl fakeroot config.system.build.nixos-prepare-root ] ++ stdenv.initialPath;
+  binPath = with pkgs; makeBinPath (
+    [ rsync
+      utillinux
+      parted
+      e2fsprogs
+      lkl
+      config.system.build.nixos-install
+      config.system.build.nixos-enter
+      nix
+    ] ++ stdenv.initialPath);
 
   # I'm preserving the line below because I'm going to search for it across nixpkgs to consolidate
   # image building logic. The comment right below this now appears in 4 different places in nixpkgs :)
@@ -78,22 +113,35 @@ let format' = format; in let
   sources = map (x: x.source) contents;
   targets = map (x: x.target) contents;
 
+  closureInfo = pkgs.closureInfo { rootPaths = [ config.system.build.toplevel channelSources ]; };
+
   prepareImage = ''
-    export PATH=${makeSearchPathOutput "bin" "bin" prepareImageInputs}
+    export PATH=${binPath}
+
+    # Yes, mkfs.ext4 takes different units in different contexts. Fun.
+    sectorsToKilobytes() {
+      echo $(( ( "$1" * 512 ) / 1024 ))
+    }
+
+    sectorsToBytes() {
+      echo $(( "$1" * 512  ))
+    }
 
     mkdir $out
     diskImage=nixos.raw
     truncate -s ${toString diskSize}M $diskImage
 
-    ${if partitioned then ''
-      parted --script $diskImage -- mklabel msdos mkpart primary ext4 1M -1s
-      offset=$((2048*512))
+    ${partitionDiskScript}
+
+    ${if partitionTableType != "none" then ''
+      # Get start & length of the root partition in sectors to $START and $SECTORS.
+      eval $(partx $diskImage -o START,SECTORS --nr ${rootPartition} --pairs)
+
+      mkfs.${fsType} -F -L nixos $diskImage -E offset=$(sectorsToBytes $START) $(sectorsToKilobytes $SECTORS)K
     '' else ''
-      offset=0
+      mkfs.${fsType} -F -L nixos $diskImage
     ''}
 
-    mkfs.${fsType} -F -L nixos -E offset=$offset $diskImage
-
     root="$PWD/root"
     mkdir -p $root
 
@@ -126,17 +174,23 @@ let format' = format; in let
       fi
     done
 
-    # TODO: Nix really likes to chown things it creates to its current user...
-    fakeroot nixos-prepare-root $root ${channelSources} ${config.system.build.toplevel} closure
+    export HOME=$TMPDIR
+
+    # Provide a Nix database so that nixos-install can copy closures.
+    export NIX_STATE_DIR=$TMPDIR/state
+    nix-store --load-db < ${closureInfo}/registration
+
+    echo "running nixos-install..."
+    nixos-install --root $root --no-bootloader --no-root-passwd \
+      --system ${config.system.build.toplevel} --channel ${channelSources} --substituters ""
 
     echo "copying staging root to image..."
-    cptofs ${optionalString partitioned "-P 1"} -t ${fsType} -i $diskImage $root/* /
+    cptofs -p ${optionalString (partitionTableType != "none") "-P ${rootPartition}"} -t ${fsType} -i $diskImage $root/* /
   '';
 in pkgs.vmTools.runInLinuxVM (
   pkgs.runCommand name
     { preVM = prepareImage;
-      buildInputs = with pkgs; [ utillinux e2fsprogs ];
-      exportReferencesGraph = [ "closure" metaClosure ];
+      buildInputs = with pkgs; [ utillinux e2fsprogs dosfstools ];
       postVM = ''
         ${if format == "raw" then ''
           mv $diskImage $out/${filename}
@@ -149,13 +203,9 @@ in pkgs.vmTools.runInLinuxVM (
       memSize = 1024;
     }
     ''
-      ${if partitioned then ''
-        . /sys/class/block/vda1/uevent
-        mknod /dev/vda1 b $MAJOR $MINOR
-        rootDisk=/dev/vda1
-      '' else ''
-        rootDisk=/dev/vda
-      ''}
+      export PATH=${binPath}:$PATH
+
+      rootDisk=${if partitionTableType != "none" then "/dev/vda${rootPartition}" else "/dev/vda"}
 
       # Some tools assume these exist
       ln -s vda /dev/xvda
@@ -165,21 +215,22 @@ in pkgs.vmTools.runInLinuxVM (
       mkdir $mountPoint
       mount $rootDisk $mountPoint
 
+      # Create the ESP and mount it. Unlike e2fsprogs, mkfs.vfat doesn't support an
+      # '-E offset=X' option, so we can't do this outside the VM.
+      ${optionalString (partitionTableType == "efi") ''
+        mkdir -p /mnt/boot
+        mkfs.vfat -n ESP /dev/vda1
+        mount /dev/vda1 /mnt/boot
+      ''}
+
       # Install a configuration.nix
       mkdir -p /mnt/etc/nixos
       ${optionalString (configFile != null) ''
         cp ${configFile} /mnt/etc/nixos/configuration.nix
       ''}
 
-      mount --rbind /dev  $mountPoint/dev
-      mount --rbind /proc $mountPoint/proc
-      mount --rbind /sys  $mountPoint/sys
-
       # Set up core system link, GRUB, etc.
-      NIXOS_INSTALL_BOOTLOADER=1 chroot $mountPoint /nix/var/nix/profiles/system/bin/switch-to-configuration boot
-
-      # TODO: figure out if I should activate, but for now I won't
-      # chroot $mountPoint /nix/var/nix/profiles/system/activate
+      NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
 
       # The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images
       rm -f $mountPoint/etc/machine-id
diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix
index f06649e1991a..986d80ff1b99 100644
--- a/nixos/lib/make-ext4-fs.nix
+++ b/nixos/lib/make-ext4-fs.nix
@@ -7,23 +7,22 @@
 , volumeLabel
 }:
 
+let
+  sdClosureInfo = pkgs.closureInfo { rootPaths = storePaths; };
+in
+
 pkgs.stdenv.mkDerivation {
   name = "ext4-fs.img";
 
-  buildInputs = with pkgs; [e2fsprogs libfaketime perl];
-
-  # For obtaining the closure of `storePaths'.
-  exportReferencesGraph =
-    map (x: [("closure-" + baseNameOf x) x]) storePaths;
+  nativeBuildInputs = with pkgs; [e2fsprogs libfaketime perl];
 
   buildCommand =
     ''
       # Add the closures of the top-level store objects.
-      storePaths=$(perl ${pkgs.pathsFromGraph} closure-*)
+      storePaths=$(cat ${sdClosureInfo}/store-paths)
 
-      # Also include a manifest of the closures in a format suitable
-      # for nix-store --load-db.
-      printRegistration=1 perl ${pkgs.pathsFromGraph} closure-* > nix-path-registration
+      # Also include a manifest of the closures in a format suitable for nix-store --load-db.
+      cp ${sdClosureInfo}/registration nix-path-registration
 
       # Make a crude approximation of the size of the target image.
       # If the script starts failing, increase the fudge factors here.
diff --git a/nixos/lib/make-iso9660-image.nix b/nixos/lib/make-iso9660-image.nix
index 75be70dbcb2b..c6bafd48f9db 100644
--- a/nixos/lib/make-iso9660-image.nix
+++ b/nixos/lib/make-iso9660-image.nix
@@ -1,4 +1,4 @@
-{ stdenv, perl, pathsFromGraph, xorriso, syslinux
+{ stdenv, perl, closureInfo, xorriso, syslinux
 
 , # The file name of the resulting ISO image.
   isoName ? "cd.iso"
@@ -48,9 +48,9 @@ assert usbBootable -> isohybridMbrImage != "";
 stdenv.mkDerivation {
   name = isoName;
   builder = ./make-iso9660-image.sh;
-  buildInputs = [perl xorriso syslinux];
+  buildInputs = [ xorriso syslinux ];
 
-  inherit isoName bootable bootImage compressImage volumeID pathsFromGraph efiBootImage efiBootable isohybridMbrImage usbBootable;
+  inherit isoName bootable bootImage compressImage volumeID efiBootImage efiBootable isohybridMbrImage usbBootable;
 
   # !!! should use XML.
   sources = map (x: x.source) contents;
@@ -61,6 +61,5 @@ stdenv.mkDerivation {
   symlinks = map (x: x.symlink) storeContents;
 
   # For obtaining the closure of `storeContents'.
-  exportReferencesGraph =
-    map (x: [("closure-" + baseNameOf x.object) x.object]) storeContents;
+  closureInfo = closureInfo { rootPaths = map (x: x.object) storeContents; };
 }
diff --git a/nixos/lib/make-iso9660-image.sh b/nixos/lib/make-iso9660-image.sh
index c623436f6c5b..45cdef1ef4df 100644
--- a/nixos/lib/make-iso9660-image.sh
+++ b/nixos/lib/make-iso9660-image.sh
@@ -72,16 +72,15 @@ done
 
 
 # Add the closures of the top-level store objects.
-storePaths=$(perl $pathsFromGraph closure-*)
-for i in $storePaths; do
+for i in $(< $closureInfo/store-paths); do
     addPath "${i:1}" "$i"
 done
 
 
 # Also include a manifest of the closures in a format suitable for
 # nix-store --load-db.
-if [ -n "$object" ]; then
-    printRegistration=1 perl $pathsFromGraph closure-* > nix-path-registration
+if [[ ${#objects[*]} != 0 ]]; then
+    cp $closureInfo/registration nix-path-registration
     addPath "nix-path-registration" "nix-path-registration"
 fi
 
diff --git a/nixos/lib/make-squashfs.nix b/nixos/lib/make-squashfs.nix
index 4100af27becb..7ab84e47f53b 100644
--- a/nixos/lib/make-squashfs.nix
+++ b/nixos/lib/make-squashfs.nix
@@ -1,4 +1,4 @@
-{ stdenv, squashfsTools, perl, pathsFromGraph
+{ stdenv, squashfsTools, closureInfo
 
 , # The root directory of the squashfs filesystem is filled with the
   # closures of the Nix store paths listed here.
@@ -8,23 +8,18 @@
 stdenv.mkDerivation {
   name = "squashfs.img";
 
-  buildInputs = [perl squashfsTools];
-
-  # For obtaining the closure of `storeContents'.
-  exportReferencesGraph =
-    map (x: [("closure-" + baseNameOf x) x]) storeContents;
+  nativeBuildInputs = [ squashfsTools ];
 
   buildCommand =
     ''
-      # Add the closures of the top-level store objects.
-      storePaths=$(perl ${pathsFromGraph} closure-*)
+      closureInfo=${closureInfo { rootPaths = storeContents; }}
 
       # Also include a manifest of the closures in a format suitable
       # for nix-store --load-db.
-      printRegistration=1 perl ${pathsFromGraph} closure-* > nix-path-registration
+      cp $closureInfo/registration nix-path-registration
 
       # Generate the squashfs image.
-      mksquashfs nix-path-registration $storePaths $out \
+      mksquashfs nix-path-registration $(cat $closureInfo/store-paths) $out \
         -keep-as-directory -all-root -b 1048576 -comp xz -Xdict-size 100%
     '';
 }
diff --git a/nixos/lib/make-system-tarball.nix b/nixos/lib/make-system-tarball.nix
index a2a0340a6bd3..92539235be75 100644
--- a/nixos/lib/make-system-tarball.nix
+++ b/nixos/lib/make-system-tarball.nix
@@ -1,4 +1,4 @@
-{ stdenv, perl, xz, pathsFromGraph
+{ stdenv, perl, pixz, pathsFromGraph
 
 , # The file name of the resulting tarball
   fileName ? "nixos-system-${stdenv.system}"
@@ -21,14 +21,20 @@
 
   # Extra tar arguments
 , extraArgs ? ""
+  # Command used for compression
+, compressCommand ? "pixz"
+  # Extension for the compressed tarball
+, compressionExtension ? ".xz"
+  # extra inputs, like the compressor to use
+, extraInputs ? [ pixz ]
 }:
 
 stdenv.mkDerivation {
   name = "tarball";
   builder = ./make-system-tarball.sh;
-  buildInputs = [perl xz];
+  buildInputs = [ perl ] ++ extraInputs;
 
-  inherit fileName pathsFromGraph extraArgs extraCommands;
+  inherit fileName pathsFromGraph extraArgs extraCommands compressCommand;
 
   # !!! should use XML.
   sources = map (x: x.source) contents;
@@ -41,4 +47,6 @@ stdenv.mkDerivation {
   # For obtaining the closure of `storeContents'.
   exportReferencesGraph =
     map (x: [("closure-" + baseNameOf x.object) x.object]) storeContents;
+
+  extension = compressionExtension;
 }
diff --git a/nixos/lib/make-system-tarball.sh b/nixos/lib/make-system-tarball.sh
index 73a009d8488a..1a52a284a257 100644
--- a/nixos/lib/make-system-tarball.sh
+++ b/nixos/lib/make-system-tarball.sh
@@ -1,5 +1,4 @@
 source $stdenv/setup
-set -x
 
 sources_=($sources)
 targets_=($targets)
@@ -54,8 +53,8 @@ mkdir -p $out/tarball
 
 rm env-vars
 
-tar --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner -cvJf $out/tarball/$fileName.tar.xz * $extraArgs
+time tar --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner -c * $extraArgs | $compressCommand > $out/tarball/$fileName.tar${extension}
 
 mkdir -p $out/nix-support
 echo $system > $out/nix-support/system
-echo "file system-tarball $out/tarball/$fileName.tar.xz" > $out/nix-support/hydra-build-products
+echo "file system-tarball $out/tarball/$fileName.tar${extension}" > $out/nix-support/hydra-build-products
diff --git a/nixos/lib/qemu-flags.nix b/nixos/lib/qemu-flags.nix
index de355b08918c..fcdcbf1b0077 100644
--- a/nixos/lib/qemu-flags.nix
+++ b/nixos/lib/qemu-flags.nix
@@ -1,4 +1,5 @@
 # QEMU flags shared between various Nix expressions.
+{ pkgs }:
 
 {
 
@@ -7,4 +8,14 @@
       "-net vde,vlan=${toString nic},sock=$QEMU_VDE_SOCKET_${toString net}"
     ];
 
+  qemuSerialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0"
+        else if pkgs.stdenv.isArm || pkgs.stdenv.isAarch64 then "ttyAMA0"
+        else throw "Unknown QEMU serial device for system '${pkgs.stdenv.system}'";
+
+  qemuBinary = qemuPkg: {
+    "i686-linux" = "${qemuPkg}/bin/qemu-kvm";
+    "x86_64-linux" = "${qemuPkg}/bin/qemu-kvm -cpu kvm64";
+    "armv7l-linux" = "${qemuPkg}/bin/qemu-system-arm -enable-kvm -machine virt -cpu host";
+    "aarch64-linux" = "${qemuPkg}/bin/qemu-system-aarch64 -enable-kvm -machine virt,gic-version=host -cpu host";
+  }.${pkgs.stdenv.system} or (throw "Unknown QEMU binary for '${pkgs.stdenv.system}'");
 }
diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index a7ed5d1faa38..7e269b43e70f 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -146,6 +146,7 @@ sub start {
             ($self->{allowReboot} ? "" : "-no-reboot ") .
             "-monitor unix:./monitor -chardev socket,id=shell,path=./shell " .
             "-device virtio-serial -device virtconsole,chardev=shell " .
+            "-device virtio-rng-pci " .
             ($showGraphics ? "-serial stdio" : "-nographic") . " " . ($ENV{QEMU_OPTS} || "");
         chdir $self->{stateDir} or die;
         exec $self->{startCommand};
@@ -361,8 +362,8 @@ sub mustFail {
 
 
 sub getUnitInfo {
-    my ($self, $unit) = @_;
-    my ($status, $lines) = $self->execute("systemctl --no-pager show '$unit'");
+    my ($self, $unit, $user) = @_;
+    my ($status, $lines) = $self->systemctl("--no-pager show \"$unit\"", $user);
     return undef if $status != 0;
     my $info = {};
     foreach my $line (split '\n', $lines) {
@@ -372,6 +373,16 @@ sub getUnitInfo {
     return $info;
 }
 
+sub systemctl {
+    my ($self, $q, $user) = @_;
+    if ($user) {
+        $q =~ s/'/\\'/g;
+        return $self->execute("su -l $user -c \$'XDG_RUNTIME_DIR=/run/user/`id -u` systemctl --user $q'");
+    }
+
+    return $self->execute("systemctl $q");
+}
+
 # Fail if the given systemd unit is not in the "active" state.
 sub requireActiveUnit {
     my ($self, $unit) = @_;
@@ -386,16 +397,16 @@ sub requireActiveUnit {
 
 # Wait for a systemd unit to reach the "active" state.
 sub waitForUnit {
-    my ($self, $unit) = @_;
+    my ($self, $unit, $user) = @_;
     $self->nest("waiting for unit ‘$unit’", sub {
         retry sub {
-            my $info = $self->getUnitInfo($unit);
+            my $info = $self->getUnitInfo($unit, $user);
             my $state = $info->{ActiveState};
             die "unit ‘$unit’ reached state ‘$state’\n" if $state eq "failed";
             if ($state eq "inactive") {
                 # If there are no pending jobs, then assume this unit
                 # will never reach active state.
-                my ($status, $jobs) = $self->execute("systemctl list-jobs --full 2>&1");
+                my ($status, $jobs) = $self->systemctl("list-jobs --full 2>&1", $user);
                 if ($jobs =~ /No jobs/) {  # FIXME: fragile
                     # Handle the case where the unit may have started
                     # between the previous getUnitInfo() and
@@ -429,14 +440,14 @@ sub waitForFile {
 }
 
 sub startJob {
-    my ($self, $jobName) = @_;
-    $self->execute("systemctl start $jobName");
+    my ($self, $jobName, $user) = @_;
+    $self->systemctl("start $jobName", $user);
     # FIXME: check result
 }
 
 sub stopJob {
-    my ($self, $jobName) = @_;
-    $self->execute("systemctl stop $jobName");
+    my ($self, $jobName, $user) = @_;
+    $self->systemctl("stop $jobName", $user);
 }
 
 
@@ -601,7 +612,7 @@ sub waitForX {
     my ($self, $regexp) = @_;
     $self->nest("waiting for the X11 server", sub {
         retry sub {
-            my ($status, $out) = $self->execute("journalctl -b SYSLOG_IDENTIFIER=systemd | grep 'session opened'");
+            my ($status, $out) = $self->execute("journalctl -b SYSLOG_IDENTIFIER=systemd | grep 'Reached target Current graphical'");
             return 0 if $status != 0;
             ($status, $out) = $self->execute("[ -e /tmp/.X11-unix/X0 ]");
             return 1 if $status == 0;
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index 9339ba78ff0c..57acc990a48f 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -3,7 +3,11 @@
 with import ./build-vms.nix { inherit system minimal config; };
 with pkgs;
 
-rec {
+let
+  jquery-ui = callPackage ./testing/jquery-ui.nix { };
+  jquery = callPackage ./testing/jquery.nix { };
+
+in rec {
 
   inherit pkgs;
 
@@ -29,7 +33,7 @@ rec {
         cp ${./test-driver/Logger.pm} $libDir/Logger.pm
 
         wrapProgram $out/bin/nixos-test-driver \
-          --prefix PATH : "${lib.makeBinPath [ qemu vde2 netpbm coreutils ]}" \
+          --prefix PATH : "${lib.makeBinPath [ qemu_test vde2 netpbm coreutils ]}" \
           --prefix PERL5LIB : "${with perlPackages; lib.makePerlPath [ TermReadLineGnu XMLWriter IOTty FileSlurp ]}:$out/lib/perl5/site_perl"
       '';
   };
@@ -78,14 +82,26 @@ rec {
     } @ t:
 
     let
-      testDriverName = "nixos-test-driver-${name}";
+      # A standard store path to the vm monitor is built like this:
+      #   /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
+      # The max filename length of a unix domain socket is 108 bytes.
+      # This means $name can at most be 50 bytes long.
+      maxTestNameLen = 50;
+      testNameLen = builtins.stringLength name;
+
+      testDriverName = with builtins;
+        if testNameLen > maxTestNameLen then
+          abort ("The name of the test '${name}' must not be longer than ${toString maxTestNameLen} " +
+            "it's currently ${toString testNameLen} characters long.")
+        else
+          "nixos-test-driver-${name}";
 
       nodes = buildVirtualNetwork (
         t.nodes or (if t ? machine then { machine = t.machine; } else { }));
 
       testScript' =
         # Call the test script with the computed nodes.
-        if builtins.isFunction testScript
+        if lib.isFunction testScript
         then testScript { inherit nodes; }
         else testScript;
 
@@ -95,6 +111,8 @@ rec {
 
       ocrProg = tesseract_4.override { enableLanguages = [ "eng" ]; };
 
+      imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
+
       # Generate onvenience wrappers for running the test driver
       # interactively with the specified network, and for starting the
       # VMs from the command line.
@@ -112,9 +130,8 @@ rec {
           wrapProgram $out/bin/nixos-test-driver \
             --add-flags "''${vms[*]}" \
             ${lib.optionalString enableOCR
-              "--prefix PATH : '${ocrProg}/bin:${imagemagick}/bin'"} \
-            --run "testScript=\"\$(cat $out/test-script)\"" \
-            --set testScript '$testScript' \
+              "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
+            --run "export testScript=\"\$(cat $out/test-script)\"" \
             --set VLANS '${toString vlans}'
           ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
           wrapProgram $out/bin/nixos-run-vms \
@@ -132,8 +149,8 @@ rec {
       test = passMeta (runTests driver);
       report = passMeta (releaseTools.gcovReport { coverageRuns = [ test ]; });
 
-    in (if makeCoverageReport then report else test) // { 
-      inherit nodes driver test; 
+    in (if makeCoverageReport then report else test) // {
+      inherit nodes driver test;
     };
 
   runInMachine =
diff --git a/nixos/lib/testing/jquery-ui.nix b/nixos/lib/testing/jquery-ui.nix
new file mode 100644
index 000000000000..e65107a3c2fb
--- /dev/null
+++ b/nixos/lib/testing/jquery-ui.nix
@@ -0,0 +1,24 @@
+{ stdenv, fetchurl, unzip }:
+
+stdenv.mkDerivation rec {
+  name = "jquery-ui-1.11.4";
+
+  src = fetchurl {
+    url = "http://jqueryui.com/resources/download/${name}.zip";
+    sha256 = "0ciyaj1acg08g8hpzqx6whayq206fvf4whksz2pjgxlv207lqgjh";
+  };
+
+  buildInputs = [ unzip ];
+
+  installPhase =
+    ''
+      mkdir -p "$out/js"
+      cp -rv . "$out/js"
+    '';
+
+  meta = {
+    homepage = http://jqueryui.com/;
+    description = "A library of JavaScript widgets and effects";
+    platforms = stdenv.lib.platforms.all;
+  };
+}
diff --git a/nixos/lib/testing/jquery.nix b/nixos/lib/testing/jquery.nix
new file mode 100644
index 000000000000..103721cadc38
--- /dev/null
+++ b/nixos/lib/testing/jquery.nix
@@ -0,0 +1,36 @@
+{ stdenv, fetchurl, compressed ? true }:
+
+with stdenv.lib;
+
+stdenv.mkDerivation rec {
+  name = "jquery-1.11.3";
+
+  src = if compressed then
+    fetchurl {
+      url = "http://code.jquery.com/${name}.min.js";
+      sha256 = "1f4glgxxn3jnvry3dpzmazj3207baacnap5w20gr2xlk789idfgc";
+    }
+    else
+    fetchurl {
+      url = "http://code.jquery.com/${name}.js";
+      sha256 = "1v956yf5spw0156rni5z77hzqwmby7ajwdcd6mkhb6zvl36awr90";
+    };
+
+  unpackPhase = "true";
+
+  installPhase =
+    ''
+      mkdir -p "$out/js"
+      cp -v "$src" "$out/js/jquery.js"
+      ${optionalString compressed ''
+        (cd "$out/js" && ln -s jquery.js jquery.min.js)
+      ''}
+    '';
+
+  meta = with stdenv.lib; {
+    description = "JavaScript library designed to simplify the client-side scripting of HTML";
+    homepage = http://jquery.com/;
+    license = licenses.mit;
+    platforms = platforms.all;
+  };
+}
diff --git a/nixos/maintainers/option-usages.nix b/nixos/maintainers/option-usages.nix
index 7be0255b35ac..371ee7d91808 100644
--- a/nixos/maintainers/option-usages.nix
+++ b/nixos/maintainers/option-usages.nix
@@ -15,7 +15,7 @@
 #
 #   $ nix-build ./option-usage.nix --argstr testOption service.xserver.enable -A txt -o service.xserver.enable._txt
 #
-# otther target exists such as, `dotContent`, `dot`, and `pdf`.  If you are
+# Other targets exists such as `dotContent`, `dot`, and `pdf`.  If you are
 # looking for the option usage of multiple options, you can provide a list
 # as argument.
 #
@@ -35,7 +35,7 @@
 # value is replaced by a `throw` statement which is caught by the `tryEval`
 # evaluation of each option value.
 #
-# We then compare the result of the evluation of the original module, with
+# We then compare the result of the evaluation of the original module, with
 # the result of the second evaluation, and consider that the new failures are
 # caused by our mutation of the `config` argument.
 #
@@ -62,7 +62,7 @@ let
     "_module.args"
 
     # For some reasons which we yet have to investigate, some options cannot
-    # be replaced by a throw without cuasing a non-catchable failure.
+    # be replaced by a throw without causing a non-catchable failure.
     "networking.bonds"
     "networking.bridges"
     "networking.interfaces"
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index 2e67edf8ee3d..972c04453aef 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -46,7 +46,7 @@ in {
     inherit lib config;
     inherit (cfg) contents format name;
     pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
-    partitioned = config.ec2.hvm;
+    partitionTableType = if config.ec2.hvm then "legacy" else "none";
     diskSize = cfg.sizeMB;
     configFile = pkgs.writeText "configuration.nix"
       ''
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index dfc0f8d11465..347e6b9c6e0d 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -18,7 +18,7 @@ rm -f ec2-amis.nix
 
 types="hvm"
 stores="ebs"
-regions="eu-west-1 eu-west-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2 ca-central-1 ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2 sa-east-1 ap-south-1"
+regions="eu-west-1 eu-west-2 eu-west-3 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2 ca-central-1 ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2 sa-east-1 ap-south-1"
 
 for type in $types; do
     link=$stateDir/$type
diff --git a/nixos/maintainers/scripts/gce/create-gce.sh b/nixos/maintainers/scripts/gce/create-gce.sh
index ef1801fe54be..0fd26d34d07f 100755
--- a/nixos/maintainers/scripts/gce/create-gce.sh
+++ b/nixos/maintainers/scripts/gce/create-gce.sh
@@ -3,7 +3,7 @@
 
 set -euo pipefail
 
-BUCKET_NAME="${BUCKET_NAME:-nixos-images}"
+BUCKET_NAME="${BUCKET_NAME:-nixos-cloud-images}"
 TIMESTAMP="$(date +%Y%m%d%H%M)"
 export TIMESTAMP
 
@@ -19,5 +19,5 @@ img_name=$(basename "$img_path")
 img_id=$(echo "$img_name" | sed 's|.raw.tar.gz$||;s|\.|-|g;s|_|-|g')
 if ! gsutil ls "gs://${BUCKET_NAME}/$img_name"; then
   gsutil cp "$img_path" "gs://${BUCKET_NAME}/$img_name"
+  gsutil acl ch -u AllUsers:R "gs://${BUCKET_NAME}/$img_name"
 fi
-gcloud compute images create "$img_id" --source-uri "gs://${BUCKET_NAME}/$img_name"
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index 46b22fc12854..6bf8c653e113 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -10,7 +10,7 @@ with lib;
     i18n = {
       glibcLocales = mkOption {
         type = types.path;
-        default = pkgs.glibcLocales.override {
+        default = pkgs.buildPackages.glibcLocales.override {
           allLocales = any (x: x == "all") config.i18n.supportedLocales;
           locales = config.i18n.supportedLocales;
         };
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index 619f36cd5150..4101ef82f3e1 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -290,8 +290,8 @@ in
           ln -s /run/systemd/resolve/resolv.conf /run/resolvconf/interfaces/systemd
         ''}
 
-        # Make sure resolv.conf is up to date if not managed by systemd
-        ${optionalString (!config.services.resolved.enable) ''
+        # Make sure resolv.conf is up to date if not managed manually or by systemd
+        ${optionalString (!config.environment.etc?"resolv.conf") ''
           ${pkgs.openresolv}/bin/resolvconf -u
         ''}
       '';
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index ae3e17ac27b6..a20910353f34 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -28,14 +28,14 @@ with lib;
 
     nixpkgs.config.packageOverrides = pkgs: {
       dbus = pkgs.dbus.override { x11Support = false; };
-      networkmanager_fortisslvpn = pkgs.networkmanager_fortisslvpn.override { withGnome = false; };
-      networkmanager_l2tp = pkgs.networkmanager_l2tp.override { withGnome = false; };
-      networkmanager_openconnect = pkgs.networkmanager_openconnect.override { withGnome = false; };
-      networkmanager_openvpn = pkgs.networkmanager_openvpn.override { withGnome = false; };
-      networkmanager_pptp = pkgs.networkmanager_pptp.override { withGnome = false; };
-      networkmanager_vpnc = pkgs.networkmanager_vpnc.override { withGnome = false; };
-      networkmanager_iodine = pkgs.networkmanager_iodine.override { withGnome = false; };
-      pinentry = pkgs.pinentry.override { gtk2 = null; qt4 = null; };
+      networkmanager-fortisslvpn = pkgs.networkmanager-fortisslvpn.override { withGnome = false; };
+      networkmanager-l2tp = pkgs.networkmanager-l2tp.override { withGnome = false; };
+      networkmanager-openconnect = pkgs.networkmanager-openconnect.override { withGnome = false; };
+      networkmanager-openvpn = pkgs.networkmanager-openvpn.override { withGnome = false; };
+      networkmanager-vpnc = pkgs.networkmanager-vpnc.override { withGnome = false; };
+      networkmanager-iodine = pkgs.networkmanager-iodine.override { withGnome = false; };
+      pinentry = pkgs.pinentry_ncurses;
+      gobjectIntrospection = pkgs.gobjectIntrospection.override { x11Support = false; };
     };
   };
 }
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
index 7b36d4f1cbdf..c595c6932946 100644
--- a/nixos/modules/config/nsswitch.nix
+++ b/nixos/modules/config/nsswitch.nix
@@ -17,23 +17,23 @@ let
   resolved = canLoadExternalModules && config.services.resolved.enable;
 
   hostArray = [ "files" ]
-    ++ optionals mymachines [ "mymachines" ]
-    ++ optionals nssmdns [ "mdns_minimal [NOTFOUND=return]" ]
-    ++ optionals nsswins [ "wins" ]
-    ++ optionals resolved ["resolve [!UNAVAIL=return]"]
+    ++ optional mymachines "mymachines"
+    ++ optional nssmdns "mdns_minimal [NOTFOUND=return]"
+    ++ optional nsswins "wins"
+    ++ optional resolved "resolve [!UNAVAIL=return]"
     ++ [ "dns" ]
-    ++ optionals nssmdns [ "mdns" ]
-    ++ optionals myhostname ["myhostname" ];
+    ++ optional nssmdns "mdns"
+    ++ optional myhostname "myhostname";
 
   passwdArray = [ "files" ]
     ++ optional sssd "sss"
-    ++ optionals ldap [ "ldap" ]
-    ++ optionals mymachines [ "mymachines" ]
+    ++ optional ldap "ldap"
+    ++ optional mymachines "mymachines"
     ++ [ "systemd" ];
 
   shadowArray = [ "files" ]
     ++ optional sssd "sss"
-    ++ optionals ldap [ "ldap" ];
+    ++ optional ldap "ldap";
 
   servicesArray = [ "files" ]
     ++ optional sssd "sss";
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix
index a4a4d6e1a6af..4c37e8a6208c 100644
--- a/nixos/modules/config/power-management.nix
+++ b/nixos/modules/config/power-management.nix
@@ -69,9 +69,6 @@ in
 
   config = mkIf cfg.enable {
 
-    # Leftover for old setups, should be set by nixos-generate-config now
-    powerManagement.cpuFreqGovernor = mkDefault "ondemand";
-
     systemd.targets.post-resume = {
       description = "Post-Resume Actions";
       requires = [ "post-resume.service" ];
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index 8b9c3570476a..90cea47b70ae 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -45,7 +45,7 @@ let
   uid = ids.uids.pulseaudio;
   gid = ids.gids.pulseaudio;
 
-  stateDir = "/var/run/pulse";
+  stateDir = "/run/pulse";
 
   # Create pulse/client.conf even if PulseAudio is disabled so
   # that we can disable the autospawn feature in programs that
@@ -101,7 +101,8 @@ in {
           each user that tries to use the sound system. The server runs
           with user privileges. This is the recommended and most secure
           way to use PulseAudio. If true, one system-wide PulseAudio
-          server is launched on boot, running as the user "pulse".
+          server is launched on boot, running as the user "pulse", and
+          only users in the "audio" group will have access to the server.
           Please read the PulseAudio documentation for more details.
         '';
       };
@@ -213,12 +214,20 @@ in {
     (mkIf cfg.enable {
       environment.systemPackages = [ overriddenPackage ];
 
+      sound.enable = true;
+
       environment.etc = [
         { target = "asound.conf";
           source = alsaConf; }
 
         { target = "pulse/daemon.conf";
           source = writeText "daemon.conf" (lib.generators.toKeyValue {} cfg.daemon.config); }
+
+        { target = "openal/alsoft.conf";
+          source = writeText "alsoft.conf" "drivers=pulse"; }
+
+        { target = "libao.conf";
+          source = writeText "libao.conf" "default_driver=pulse"; }
       ];
 
       # Allow PulseAudio to get realtime priority using rtkit.
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 65f2e5d7af99..398660967c52 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -36,7 +36,7 @@ in
       default = {};
       description = ''
         A set of environment variables used in the global environment.
-        These variables will be set on shell initialisation.
+        These variables will be set on shell initialisation (e.g. in /etc/profile).
         The value of each variable can be either a string or a list of
         strings.  The latter is concatenated, interspersed with colon
         characters.
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index d3212d931605..361151665018 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -109,7 +109,6 @@ in
         "/sbin"
         "/share/applications"
         "/share/desktop-directories"
-        "/share/doc"
         "/share/emacs"
         "/share/icons"
         "/share/menus"
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index a4715175cc95..621ca36fb6b8 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -35,6 +35,7 @@ let
 
       name = mkOption {
         type = types.str;
+        apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
         description = ''
           The name of the user account. If undefined, the name of the
           attribute set will be used.
@@ -91,6 +92,7 @@ let
 
       group = mkOption {
         type = types.str;
+        apply = x: assert (builtins.stringLength x < 32 || abort "Group name '${x}' is longer than 31 characters which is not allowed!"); x;
         default = "nogroup";
         description = "The user's primary group.";
       };
@@ -502,9 +504,6 @@ in {
       };
     };
 
-    # Install all the user shells
-    environment.systemPackages = systemShells;
-
     users.groups = {
       root.gid = ids.gids.root;
       wheel.gid = ids.gids.wheel;
@@ -529,6 +528,9 @@ in {
 
     system.activationScripts.users = stringAfter [ "stdio" ]
       ''
+        install -m 0700 -d /root
+        install -m 0755 -d /home
+
         ${pkgs.perl}/bin/perl -w \
           -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl \
           -I${pkgs.perlPackages.JSON}/lib/perl5/site_perl \
@@ -538,14 +540,29 @@ in {
     # for backwards compatibility
     system.activationScripts.groups = stringAfter [ "users" ] "";
 
-    environment.etc."subuid" = {
-      text = subuidFile;
-      mode = "0644";
-    };
-    environment.etc."subgid" = {
-      text = subgidFile;
-      mode = "0644";
-    };
+    # Install all the user shells
+    environment.systemPackages = systemShells;
+
+    environment.etc = {
+      "subuid" = {
+        text = subuidFile;
+        mode = "0644";
+      };
+      "subgid" = {
+        text = subgidFile;
+        mode = "0644";
+      };
+    } // (mapAttrs' (name: { packages, ... }: {
+      name = "profiles/per-user/${name}";
+      value.source = pkgs.buildEnv {
+        name = "user-environment";
+        paths = packages;
+        inherit (config.environment) pathsToLink extraOutputsToInstall;
+        inherit (config.system.path) ignoreCollisions postBuild;
+      };
+    }) (filterAttrs (_: u: u.packages != []) cfg.users));
+
+    environment.profiles = [ "/etc/profiles/per-user/$USER" ];
 
     assertions = [
       { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
@@ -576,20 +593,4 @@ in {
 
   };
 
-  imports =
-    [ (mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ])
-      (mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ])
-      {
-        environment = {
-          etc = mapAttrs' (name: { packages, ... }: {
-            name = "per-user-pkgs/${name}";
-            value.source = pkgs.symlinkJoin {
-              name = "per-user-pkgs.${name}";
-              paths = packages;
-            };
-          }) (filterAttrs (_: { packages, ... }: packages != []) cfg.users);
-          profiles = ["/etc/per-user-pkgs/$LOGNAME"];
-        };
-      }
-    ];
 }
diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix
index ad41ad4f3d7c..ae1b0a6c8e11 100644
--- a/nixos/modules/config/zram.nix
+++ b/nixos/modules/config/zram.nix
@@ -93,7 +93,7 @@ in
             serviceConfig = {
               Type = "oneshot";
               RemainAfterExit = true;
-              ExecStop = "${pkgs.stdenv.shell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
+              ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
             };
             script = ''
               set -u
diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
index afaa65f6ce35..b61acf1815d9 100644
--- a/nixos/modules/hardware/all-firmware.nix
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -37,8 +37,9 @@ in {
       hardware.firmware = with pkgs; [
         firmwareLinuxNonfree
         intel2200BGFirmware
-        rtl8723bs-firmware
         rtl8192su-firmware
+      ] ++ optionals (versionOlder config.boot.kernelPackages.kernel.version "4.13") [
+        rtl8723bs-firmware
       ];
     })
     (mkIf cfg.enableAllFirmware {
diff --git a/nixos/modules/hardware/digitalbitbox.nix b/nixos/modules/hardware/digitalbitbox.nix
new file mode 100644
index 000000000000..0888cfbef2a8
--- /dev/null
+++ b/nixos/modules/hardware/digitalbitbox.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.digitalbitbox;
+in
+
+{
+  options.hardware.digitalbitbox = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables udev rules for Digital Bitbox devices.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.digitalbitbox;
+      defaultText = "pkgs.digitalbitbox";
+      description = "The Digital Bitbox package to use. This can be used to install a package with udev rules that differ from the defaults.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/hardware/onlykey.nix b/nixos/modules/hardware/onlykey.nix
new file mode 100644
index 000000000000..b6820fe01911
--- /dev/null
+++ b/nixos/modules/hardware/onlykey.nix
@@ -0,0 +1,33 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+
+  ####### interface
+
+  options = {
+
+    hardware.onlykey = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable OnlyKey device (https://crp.to/p/) support.
+        '';
+      };
+    };
+
+  };
+
+  ## As per OnlyKey's documentation piece (hhttps://docs.google.com/document/d/1Go_Rs218fKUx-j_JKhddbSVTqY6P0vQO831t2MKCJC8),
+  ## it is important to add udev rule for OnlyKey for it to work on Linux
+
+  ####### implementation
+
+  config = mkIf config.hardware.onlykey.enable {
+    services.udev.extraRules = builtin.readFile ./onlykey.udev;
+  };
+
+
+}
diff --git a/nixos/modules/hardware/onlykey.udev b/nixos/modules/hardware/onlykey.udev
new file mode 100644
index 000000000000..6583530e5684
--- /dev/null
+++ b/nixos/modules/hardware/onlykey.udev
@@ -0,0 +1,4 @@
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", GROUP+="plugdev"
+KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", GROUP+="plugdev"
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
index c2c36f02a143..b371af353cf9 100644
--- a/nixos/modules/hardware/opengl.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -14,7 +14,6 @@ let
     name = "mesa-drivers+txc-${p.mesa_drivers.version}";
     paths =
       [ p.mesa_drivers
-        p.mesa_drivers.out # mainly for libGL
         (if cfg.s3tcSupport then p.libtxc_dxtn else p.libtxc_dxtn_s2tc)
       ];
   };
@@ -33,82 +32,92 @@ in
 
 {
   options = {
-    hardware.opengl.enable = mkOption {
-      description = "Whether this configuration requires OpenGL.";
-      type = types.bool;
-      default = false;
-      internal = true;
-    };
-
-    hardware.opengl.driSupport = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to enable accelerated OpenGL rendering through the
-        Direct Rendering Interface (DRI).
-      '';
-    };
-
-    hardware.opengl.driSupport32Bit = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        On 64-bit systems, whether to support Direct Rendering for
-        32-bit applications (such as Wine).  This is currently only
-        supported for the <literal>nvidia</literal> and 
-        <literal>ati_unfree</literal> drivers, as well as
-        <literal>Mesa</literal>.
-      '';
-    };
-
-    hardware.opengl.s3tcSupport = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Make S3TC(S3 Texture Compression) via libtxc_dxtn available
-        to OpenGL drivers instead of the patent-free S2TC replacement.
-
-        Using this library may require a patent license depending on your location.
-      '';
-    };
 
-    hardware.opengl.package = mkOption {
-      type = types.package;
-      internal = true;
-      description = ''
-        The package that provides the OpenGL implementation.
-      '';
-    };
-
-    hardware.opengl.package32 = mkOption {
-      type = types.package;
-      internal = true;
-      description = ''
-        The package that provides the 32-bit OpenGL implementation on
-        64-bit systems. Used when <option>driSupport32Bit</option> is
-        set.
-      '';
-    };
-
-    hardware.opengl.extraPackages = mkOption {
-      type = types.listOf types.package;
-      default = [];
-      example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau intel-ocl ]";
-      description = ''
-        Additional packages to add to OpenGL drivers. This can be used
-        to add OpenCL drivers, VA-API/VDPAU drivers etc.
-      '';
-    };
-
-    hardware.opengl.extraPackages32 = mkOption {
-      type = types.listOf types.package;
-      default = [];
-      example = literalExample "with pkgs.pkgsi686Linux; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]";
-      description = ''
-        Additional packages to add to 32-bit OpenGL drivers on
-        64-bit systems. Used when <option>driSupport32Bit</option> is
-        set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
-      '';
+    hardware.opengl = {
+      enable = mkOption {
+        description = ''
+          Whether to enable OpenGL drivers. This is needed to enable
+          OpenGL support in X11 systems, as well as for Wayland compositors
+          like sway, way-cooler and Weston. It is enabled by default
+          by the corresponding modules, so you do not usually have to
+          set it yourself, only if there is no module for your wayland
+          compositor of choice. See services.xserver.enable,
+          programs.sway.enable, and programs.way-cooler.enable.
+        '';
+        type = types.bool;
+        default = false;
+      };
+
+      driSupport = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable accelerated OpenGL rendering through the
+          Direct Rendering Interface (DRI).
+        '';
+      };
+
+      driSupport32Bit = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          On 64-bit systems, whether to support Direct Rendering for
+          32-bit applications (such as Wine).  This is currently only
+          supported for the <literal>nvidia</literal> and
+          <literal>ati_unfree</literal> drivers, as well as
+          <literal>Mesa</literal>.
+        '';
+      };
+
+      s3tcSupport = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Make S3TC(S3 Texture Compression) via libtxc_dxtn available
+          to OpenGL drivers instead of the patent-free S2TC replacement.
+
+          Using this library may require a patent license depending on your location.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        internal = true;
+        description = ''
+          The package that provides the OpenGL implementation.
+        '';
+      };
+
+      package32 = mkOption {
+        type = types.package;
+        internal = true;
+        description = ''
+          The package that provides the 32-bit OpenGL implementation on
+          64-bit systems. Used when <option>driSupport32Bit</option> is
+          set.
+        '';
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau intel-ocl ]";
+        description = ''
+          Additional packages to add to OpenGL drivers. This can be used
+          to add OpenCL drivers, VA-API/VDPAU drivers etc.
+        '';
+      };
+
+      extraPackages32 = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "with pkgs.pkgsi686Linux; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]";
+        description = ''
+          Additional packages to add to 32-bit OpenGL drivers on
+          64-bit systems. Used when <option>driSupport32Bit</option> is
+          set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
+        '';
+      };
     };
 
   };
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
index 5cc96d8bd074..50af022b93c8 100644
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -15,13 +15,19 @@ let
 
   opengl = config.hardware.opengl;
 
+  kernel = pkgs.linux_4_9.override {
+    extraConfig = ''
+      KALLSYMS_ALL y
+    '';
+  };
+
 in
 
 {
 
   config = mkIf enabled {
 
-    nixpkgs.config.xorg.abiCompat = "1.18";
+    nixpkgs.config.xorg.abiCompat = "1.19";
 
     services.xserver.drivers = singleton
       { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; };
@@ -31,6 +37,9 @@ in
 
     boot.extraModulePackages = [ package ];
 
+    boot.kernelPackages =
+      pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor kernel);
+
     boot.blacklistedKernelModules = [ "radeon" ];
 
     hardware.firmware = [ package ];
@@ -38,10 +47,15 @@ in
     system.activationScripts.setup-amdgpu-pro = ''
       mkdir -p /run/lib
       ln -sfn ${package}/lib ${package.libCompatDir}
+      ln -sfn ${package} /run/amdgpu-pro
     '' + optionalString opengl.driSupport32Bit ''
       ln -sfn ${package32}/lib ${package32.libCompatDir}
     '';
 
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "KALLSYMS_ALL")
+    ];
+
     environment.etc = {
       "amd/amdrc".source = package + "/etc/amd/amdrc";
       "amd/amdapfxx.blb".source = package + "/etc/amd/amdapfxx.blb";
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 50c085dd7ee2..eb1952280331 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -16,8 +16,6 @@ let
         kernelPackages.nvidia_x11
     else if elem "nvidiaBeta" drivers then
         kernelPackages.nvidia_x11_beta
-    else if elem "nvidiaLegacy173" drivers then
-      kernelPackages.nvidia_x11_legacy173
     else if elem "nvidiaLegacy304" drivers then
       kernelPackages.nvidia_x11_legacy304
     else if elem "nvidiaLegacy340" drivers then
@@ -27,13 +25,6 @@ let
   nvidia_x11 = nvidiaForKernel config.boot.kernelPackages;
   nvidia_libs32 = (nvidiaForKernel pkgs_i686.linuxPackages).override { libsOnly = true; kernel = null; };
 
-  nvidiaPackage = nvidia: pkgs:
-    if !nvidia.useGLVND then nvidia.out
-    else pkgs.buildEnv {
-      name = "nvidia-libs";
-      paths = [ pkgs.libglvnd nvidia.out ];
-    };
-
   enabled = nvidia_x11 != null;
 in
 
@@ -59,8 +50,8 @@ in
       source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
     };
 
-    hardware.opengl.package = nvidiaPackage nvidia_x11 pkgs;
-    hardware.opengl.package32 = nvidiaPackage nvidia_libs32 pkgs_i686;
+    hardware.opengl.package = nvidia_x11.out;
+    hardware.opengl.package32 = nvidia_libs32.out;
 
     environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings ]
       ++ lib.filter (p: p != null) [ nvidia_x11.persistenced ];
@@ -75,10 +66,10 @@ in
     # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
     services.udev.extraRules =
       ''
-        KERNEL=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
-        KERNEL=="nvidia_modeset", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
-        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) %n'"
-        KERNEL=="nvidia_uvm", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
+        KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
+        KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
+        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) %n'"
+        KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
       '';
 
     boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ];
diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix
index ddb00f174d1a..01cfe8a02e10 100644
--- a/nixos/modules/installer/cd-dvd/channel.nix
+++ b/nixos/modules/installer/cd-dvd/channel.nix
@@ -12,7 +12,7 @@ let
   # CD.  These are installed into the "nixos" channel of the root
   # user, as expected by nixos-rebuild/nixos-install. FIXME: merge
   # with make-channel.nix.
-  channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}"
+  channelSources = pkgs.runCommand "nixos-${config.system.nixos.version}"
     { }
     ''
       mkdir -p $out
@@ -21,7 +21,9 @@ let
       if [ ! -e $out/nixos/nixpkgs ]; then
         ln -s . $out/nixos/nixpkgs
       fi
-      echo -n ${config.system.nixosVersionSuffix} > $out/nixos/.version-suffix
+      echo -n ${config.system.nixos.revision} > $out/nixos/.git-revision
+      echo -n ${config.system.nixos.versionSuffix} > $out/nixos/.version-suffix
+      echo ${config.system.nixos.versionSuffix} | sed -e s/pre// > $out/nixos/svn-revision
     '';
 
 in
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
index 2569860a098f..756c8751d00e 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -16,7 +16,7 @@ with lib;
     ];
 
   # ISO naming.
-  isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixosLabel}-${pkgs.stdenv.system}.iso";
+  isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixos.label}-${pkgs.stdenv.system}.iso";
 
   isoImage.volumeID = substring 0 11 "NIXOS_ISO";
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
index 5725938465f5..4c4e69d60d9c 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
@@ -69,7 +69,7 @@ with lib;
   in ''
     mkdir -p /root/Desktop
     ln -sfT ${desktopFile} /root/Desktop/nixos-manual.desktop
-    cp ${pkgs.gnome3.gnome_terminal}/share/applications/gnome-terminal.desktop /root/Desktop/gnome-terminal.desktop
+    cp ${pkgs.gnome3.gnome-terminal}/share/applications/gnome-terminal.desktop /root/Desktop/gnome-terminal.desktop
     chmod a+rx /root/Desktop/gnome-terminal.desktop
     cp ${pkgs.gparted}/share/applications/gparted.desktop /root/Desktop/gparted.desktop
     chmod a+rx /root/Desktop/gparted.desktop
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index a039f7fdcb6e..e7cbf415a223 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -39,31 +39,31 @@ let
     DEFAULT boot
 
     LABEL boot
-    MENU LABEL NixOS ${config.system.nixosLabel}${config.isoImage.appendToMenuLabel}
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
 
     # A variant to boot with 'nomodeset'
     LABEL boot-nomodeset
-    MENU LABEL NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (nomodeset)
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (nomodeset)
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
 
     # A variant to boot with 'copytoram'
     LABEL boot-copytoram
-    MENU LABEL NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (copytoram)
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (copytoram)
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
 
     # A variant to boot with verbose logging to the console
     LABEL boot-nomodeset
-    MENU LABEL NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (debug)
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug)
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
   '';
 
   isolinuxMemtest86Entry = ''
@@ -82,35 +82,35 @@ let
     mkdir -p $out/loader/entries
 
     cat << EOF > $out/loader/entries/nixos-iso.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel}
-    linux /boot/bzImage
-    initrd /boot/initrd
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
     EOF
 
     # A variant to boot with 'nomodeset'
     cat << EOF > $out/loader/entries/nixos-iso-nomodeset.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel}
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
     version nomodeset
-    linux /boot/bzImage
-    initrd /boot/initrd
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset
     EOF
 
     # A variant to boot with 'copytoram'
     cat << EOF > $out/loader/entries/nixos-iso-copytoram.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel}
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
     version copytoram
-    linux /boot/bzImage
-    initrd /boot/initrd
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram
     EOF
 
     # A variant to boot with verbose logging to the console
     cat << EOF > $out/loader/entries/nixos-iso-debug.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (debug)
-    linux /boot/bzImage
-    initrd /boot/initrd
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug)
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7
     EOF
 
@@ -127,8 +127,8 @@ let
       mkdir ./contents && cd ./contents
       cp -rp "${efiDir}"/* .
       mkdir ./boot
-      cp -p "${config.boot.kernelPackages.kernel}/bzImage" \
-        "${config.system.build.initialRamdisk}/initrd" ./boot/
+      cp -p "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}" \
+        "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}" ./boot/
       touch --date=@0 ./*
 
       usage_size=$(du -sb --apparent-size . | tr -cd '[:digit:]')
@@ -331,8 +331,7 @@ in
         config.system.build.toplevel.drvPath;
 
     # Create the squashfs image that contains the Nix store.
-    system.build.squashfsStore = import ../../../lib/make-squashfs.nix {
-      inherit (pkgs) stdenv squashfsTools perl pathsFromGraph;
+    system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
       storeContents = config.isoImage.storeContents;
     };
 
@@ -346,11 +345,11 @@ in
           };
           target = "/isolinux/isolinux.cfg";
         }
-        { source = config.boot.kernelPackages.kernel + "/bzImage";
-          target = "/boot/bzImage";
+        { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
+          target = "/boot/" + config.system.boot.loader.kernelFile;
         }
-        { source = config.system.build.initialRamdisk + "/initrd";
-          target = "/boot/initrd";
+        { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile;
+          target = "/boot/" + config.system.boot.loader.initrdFile;
         }
         { source = config.system.build.squashfsStore;
           target = "/nix-store.squashfs";
@@ -361,7 +360,7 @@ in
         { source = config.isoImage.splashImage;
           target = "/isolinux/background.png";
         }
-        { source = pkgs.writeText "version" config.system.nixosVersion;
+        { source = pkgs.writeText "version" config.system.nixos.label;
           target = "/version.txt";
         }
       ] ++ optionals config.isoImage.makeEfiBootable [
@@ -383,11 +382,8 @@ in
     boot.loader.timeout = 10;
 
     # Create the ISO image.
-    system.build.isoImage = import ../../../lib/make-iso9660-image.nix ({
-      inherit (pkgs) stdenv perl pathsFromGraph xorriso syslinux;
-
+    system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ({
       inherit (config.isoImage) isoName compressImage volumeID contents;
-
       bootable = true;
       bootImage = "/isolinux/isolinux.bin";
     } // optionalAttrs config.isoImage.makeUsbBootable {
diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
index efb9ba39bcd4..ddf91a5656c7 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
@@ -21,12 +21,10 @@ 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.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_latest;
 
   # The serial ports listed here are:
@@ -42,8 +40,17 @@ in
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
         kernel=u-boot-rpi3.bin
+
+        # Boot in 64-bit mode.
         arm_control=0x200
+
+        # U-Boot used to need this to work, regardless of whether UART is actually used or not.
+        # TODO: check when/if this can be removed.
         enable_uart=1
+
+        # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
+        # when attempting to show low-voltage or overtemperature warnings.
+        avoid_warnings=1
       '';
       in ''
         (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/boot/)
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 880a6bf2e1e8..891923234dda 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -21,12 +21,10 @@ 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.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_latest;
   # The serial ports listed here are:
   # - ttyS0: for Tegra (Jetson TK1)
@@ -42,11 +40,18 @@ in
   sdImage = {
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
+        # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
+        # when attempting to show low-voltage or overtemperature warnings.
+        avoid_warnings=1
+
         [pi2]
         kernel=u-boot-rpi2.bin
 
         [pi3]
         kernel=u-boot-rpi3.bin
+
+        # U-Boot used to need this to work, regardless of whether UART is actually used or not.
+        # TODO: check when/if this can be removed.
         enable_uart=1
       '';
       in ''
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index eb676eae05e8..212013b5e289 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -21,12 +21,10 @@ 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.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_rpi;
 
   # FIXME: this probably should be in installation-device.nix
diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix
index 23312c073d56..c091923de60f 100644
--- a/nixos/modules/installer/cd-dvd/sd-image.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image.nix
@@ -20,6 +20,20 @@ let
 in
 {
   options.sdImage = {
+    imageName = mkOption {
+      default = "${config.sdImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.system}.img";
+      description = ''
+        Name of the generated image file.
+      '';
+    };
+
+    imageBaseName = mkOption {
+      default = "nixos-sd-image";
+      description = ''
+        Prefix of the name of the generated image file.
+      '';
+    };
+
     storePaths = mkOption {
       type = with types; listOf package;
       example = literalExample "[ pkgs.stdenv ]";
@@ -61,19 +75,25 @@ in
     sdImage.storePaths = [ config.system.build.toplevel ];
 
     system.build.sdImage = pkgs.stdenv.mkDerivation {
-      name = "sd-image-${pkgs.stdenv.system}.img";
+      name = config.sdImage.imageName;
 
       buildInputs = with pkgs; [ dosfstools e2fsprogs mtools libfaketime utillinux ];
 
       buildCommand = ''
+        mkdir -p $out/nix-support $out/sd-image
+        export img=$out/sd-image/${config.sdImage.imageName}
+
+        echo "${pkgs.stdenv.system}" > $out/nix-support/system
+        echo "file sd-image $img" >> $out/nix-support/hydra-build-products
+
         # Create the image file sized to fit /boot and /, plus 20M of slack
         rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }')
         bootSizeBlocks=$((${toString config.sdImage.bootSize} * 1024 * 1024 / 512))
         imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 20 * 1024 * 1024))
-        truncate -s $imageSize $out
+        truncate -s $imageSize $img
 
         # type=b is 'W95 FAT32', type=83 is 'Linux'.
-        sfdisk $out <<EOF
+        sfdisk $img <<EOF
             label: dos
             label-id: 0x2178694e
 
@@ -82,11 +102,11 @@ in
         EOF
 
         # Copy the rootfs into the SD image
-        eval $(partx $out -o START,SECTORS --nr 2 --pairs)
-        dd conv=notrunc if=${rootfsImage} of=$out seek=$START count=$SECTORS
+        eval $(partx $img -o START,SECTORS --nr 2 --pairs)
+        dd conv=notrunc if=${rootfsImage} of=$img seek=$START count=$SECTORS
 
         # Create a FAT32 /boot partition of suitable size into bootpart.img
-        eval $(partx $out -o START,SECTORS --nr 1 --pairs)
+        eval $(partx $img -o START,SECTORS --nr 1 --pairs)
         truncate -s $((SECTORS * 512)) bootpart.img
         faketime "1970-01-01 00:00:00" mkfs.vfat -i 0x2178694e -n NIXOS_BOOT bootpart.img
 
@@ -96,7 +116,7 @@ in
 
         # Copy the populated /boot into the SD image
         (cd boot; mcopy -bpsvm -i ../bootpart.img ./* ::)
-        dd conv=notrunc if=bootpart.img of=$out seek=$START count=$SECTORS
+        dd conv=notrunc if=bootpart.img of=$img seek=$START count=$SECTORS
       '';
     };
 
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
index ba84cd51098f..6d4ba96dba0c 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
@@ -54,7 +54,7 @@ in
   environment.systemPackages =
     [ pkgs.w3m # needed for the manual anyway
       pkgs.testdisk # useful for repairing boot problems
-      pkgs.mssys # for writing Microsoft boot sectors / MBRs
+      pkgs.ms-sys # for writing Microsoft boot sectors / MBRs
       pkgs.parted
       pkgs.ddrescue
       pkgs.ccrypt
diff --git a/nixos/modules/installer/cd-dvd/system-tarball.nix b/nixos/modules/installer/cd-dvd/system-tarball.nix
index 1962a1959ead..e72d4a5b4910 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball.nix
@@ -8,7 +8,7 @@ with lib;
 
 let
 
-  versionFile = pkgs.writeText "nixos-version" config.system.nixosVersion;
+  versionFile = pkgs.writeText "nixos-label" config.system.nixos.label;
 
 in
 
@@ -58,8 +58,8 @@ in
     # Individual files to be included on the CD, outside of the Nix
     # store on the CD.
     tarball.contents =
-      [ { source = config.system.build.initialRamdisk + "/initrd";
-          target = "/boot/initrd";
+      [ { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile;
+          target = "/boot/" + config.system.boot.loader.initrdFile;
         }
         { source = versionFile;
           target = "/nixos-version.txt";
diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix
index 0f6046339b37..a4eda3c52dce 100644
--- a/nixos/modules/installer/netboot/netboot.nix
+++ b/nixos/modules/installer/netboot/netboot.nix
@@ -18,17 +18,17 @@ with lib;
 
   };
 
-  config = {
-
-    boot.loader.grub.version = 2;
-
+  config = rec {
     # Don't build the GRUB menu builder script, since we don't need it
     # here and it causes a cyclic dependency.
     boot.loader.grub.enable = false;
 
     # !!! Hack - attributes expected by other modules.
-    system.boot.loader.kernelFile = "bzImage";
-    environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ];
+    environment.systemPackages = [ pkgs.grub2_efi ]
+      ++ (if pkgs.stdenv.system == "aarch64-linux"
+          then []
+          else [ pkgs.grub2 pkgs.syslinux ]);
+    system.boot.loader.kernelFile = pkgs.stdenv.platform.kernelTarget;
 
     fileSystems."/" =
       { fsType = "tmpfs";
@@ -67,7 +67,7 @@ with lib;
 
     # Create the squashfs image that contains the Nix store.
     system.build.squashfsStore = import ../../../lib/make-squashfs.nix {
-      inherit (pkgs) stdenv squashfsTools perl pathsFromGraph;
+      inherit (pkgs) stdenv squashfsTools closureInfo;
       storeContents = config.netboot.storeContents;
     };
 
@@ -84,7 +84,12 @@ with lib;
         ];
     };
 
-    system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" "#!ipxe\nkernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}\ninitrd initrd\nboot";
+    system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
+      #!ipxe
+      kernel ${pkgs.stdenv.platform.kernelTarget} init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+      initrd initrd
+      boot
+    '';
 
     boot.loader.timeout = 10;
 
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 321fb9a2030a..6bb556a0123c 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,5 +1,6 @@
 {
-  x86_64-linux = "/nix/store/b4s1gxiis1ryvybnjhdjvgc5sr1nq0ys-nix-1.11.15";
-  i686-linux = "/nix/store/kgb5hs7qw13bvb6icramv1ry9dard3h9-nix-1.11.15";
-  x86_64-darwin = "/nix/store/dgwz3dxdzs2wwd7pg7cdhvl8rv0qpnbj-nix-1.11.15";
+  x86_64-linux = "/nix/store/2gk7rk2sx2dkmsjr59gignrfdmya8f6s-nix-2.0.1";
+  i686-linux = "/nix/store/5160glkphiv13qggnivyidg8r0491pbl-nix-2.0.1";
+  aarch64-linux = "/nix/store/jk29zz3ns9vdkkclcyzzkpzp8dhv1x3i-nix-2.0.1";
+  x86_64-darwin = "/nix/store/4a9czmrpd4hf3r80zcmga2c2lm3hbbvv-nix-2.0.1";
 }
diff --git a/nixos/modules/installer/tools/nixos-enter.sh b/nixos/modules/installer/tools/nixos-enter.sh
new file mode 100644
index 000000000000..679391189612
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-enter.sh
@@ -0,0 +1,60 @@
+#! @shell@
+
+set -e
+
+# Re-exec ourselves in a private mount namespace so that our bind
+# mounts get cleaned up automatically.
+if [ -z "$NIXOS_ENTER_REEXEC" ]; then
+    export NIXOS_ENTER_REEXEC=1
+    if [ "$(id -u)" != 0 ]; then
+        extraFlags="-r"
+    fi
+    exec unshare --fork --mount --uts --mount-proc --pid $extraFlags -- "$0" "$@"
+else
+    mount --make-rprivate /
+fi
+
+mountPoint=/mnt
+system=/nix/var/nix/profiles/system
+command=($system/sw/bin/bash "--login")
+
+while [ "$#" -gt 0 ]; do
+    i="$1"; shift 1
+    case "$i" in
+        --root)
+            mountPoint="$1"; shift 1
+            ;;
+        --system)
+            system="$1"; shift 1
+            ;;
+        --help)
+            exec man nixos-enter
+            exit 1
+            ;;
+        --command|-c)
+            command=($system/sw/bin/bash "-c" "$1")
+            shift 1
+            ;;
+        --)
+            command=("$@")
+            break
+            ;;
+        *)
+            echo "$0: unknown option \`$i'"
+            exit 1
+            ;;
+    esac
+done
+
+if [[ ! -e $mountPoint/etc/NIXOS ]]; then
+    echo "$0: '$mountPoint' is not a NixOS installation" >&2
+    exit 126
+fi
+
+mkdir -m 0755 -p "$mountPoint/dev"
+mount --rbind /dev "$mountPoint/dev"
+
+# Run the activation script. Set $LOCALE_ARCHIVE to supress some Perl locale warnings.
+LOCALE_ARCHIVE=$system/sw/lib/locale/locale-archive chroot "$mountPoint" "$system/activate" >&2 || true
+
+exec chroot "$mountPoint" "${command[@]}"
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 7c737e84de0a..14c611e18bc3 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -585,7 +585,6 @@ $bootLoaderConfig
 
   # Some programs need SUID wrappers, can be configured further or are
   # started in user sessions.
-  # programs.bash.enableCompletion = true;
   # programs.mtr.enable = true;
   # programs.gnupg.agent = { enable = true; enableSSHSupport = true; };
 
@@ -603,6 +602,10 @@ $bootLoaderConfig
   # Enable CUPS to print documents.
   # services.printing.enable = true;
 
+  # Enable sound.
+  # sound.enable = true;
+  # hardware.pulseaudio.enable = true;
+
   # Enable the X11 windowing system.
   # services.xserver.enable = true;
   # services.xserver.layout = "us";
@@ -625,7 +628,7 @@ $bootLoaderConfig
   # compatible, in order to avoid breaking some software such as database
   # servers. You should change this only after NixOS release notes say you
   # should.
-  system.stateVersion = "${\(qw(@nixosRelease@))}"; # Did you read the comment?
+  system.stateVersion = "${\(qw(@release@))}"; # Did you read the comment?
 
 }
 EOF
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index f994d5b4bde1..22c1e0fe9a34 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -1,35 +1,23 @@
 #! @shell@
 
-# - make Nix store etc.
-# - copy closure of Nix to target device
-# - register validity
-# - with a chroot to the target device:
-#   * nix-env -p /nix/var/nix/profiles/system -i <nix-expr for the configuration>
-#   * install the boot loader
+set -e
+shopt -s nullglob
+
+export PATH=@path@:$PATH
 
 # Ensure a consistent umask.
 umask 0022
 
-# Re-exec ourselves in a private mount namespace so that our bind
-# mounts get cleaned up automatically.
-if [ "$(id -u)" = 0 ]; then
-    if [ -z "$NIXOS_INSTALL_REEXEC" ]; then
-        export NIXOS_INSTALL_REEXEC=1
-        exec unshare --mount --uts -- "$0" "$@"
-    else
-        mount --make-rprivate /
-    fi
-fi
-
 # Parse the command line for the -I flag
 extraBuildFlags=()
-chrootCommand=(/run/current-system/sw/bin/bash)
-buildUsersGroup="nixbld"
+
+mountPoint=/mnt
+channelPath=
 
 while [ "$#" -gt 0 ]; do
     i="$1"; shift 1
     case "$i" in
-        --max-jobs|-j|--cores|-I)
+        --max-jobs|-j|--cores|-I|--substituters)
             j="$1"; shift 1
             extraBuildFlags+=("$i" "$j")
             ;;
@@ -41,9 +29,11 @@ while [ "$#" -gt 0 ]; do
         --root)
             mountPoint="$1"; shift 1
             ;;
-        --closure)
-            closure="$1"; shift 1
-            buildUsersGroup=""
+        --system|--closure)
+            system="$1"; shift 1
+            ;;
+        --channel)
+            channelPath="$1"; shift 1
             ;;
         --no-channel-copy)
             noChannelCopy=1
@@ -57,17 +47,13 @@ while [ "$#" -gt 0 ]; do
         --show-trace)
             extraBuildFlags+=("$i")
             ;;
-        --chroot)
-            runChroot=1
-            if [[ "$@" != "" ]]; then
-                chrootCommand=("$@")
-            fi
-            break
-            ;;
         --help)
             exec man nixos-install
             exit 1
             ;;
+        --debug)
+            set -x
+            ;;
         *)
             echo "$0: unknown option \`$i'"
             exit 1
@@ -75,132 +61,83 @@ while [ "$#" -gt 0 ]; do
     esac
 done
 
-set -e
-shopt -s nullglob
-
-if test -z "$mountPoint"; then
-    mountPoint=/mnt
-fi
-
 if ! test -e "$mountPoint"; then
     echo "mount point $mountPoint doesn't exist"
     exit 1
 fi
 
 # Get the path of the NixOS configuration file.
-if test -z "$NIXOS_CONFIG"; then
-    NIXOS_CONFIG=/etc/nixos/configuration.nix
+if [[ -z $NIXOS_CONFIG ]]; then
+    NIXOS_CONFIG=$mountPoint/etc/nixos/configuration.nix
 fi
 
-if [ ! -e "$mountPoint/$NIXOS_CONFIG" ] && [ -z "$closure" ]; then
-    echo "configuration file $mountPoint/$NIXOS_CONFIG doesn't exist"
+if [[ ${NIXOS_CONFIG:0:1} != / ]]; then
+    echo "$0: \$NIXOS_CONFIG is not an absolute path"
     exit 1
 fi
 
-
-# Builds will use users that are members of this group
-extraBuildFlags+=(--option "build-users-group" "$buildUsersGroup")
-
-# Inherit binary caches from the host
-# TODO: will this still work with Nix 1.12 now that it has no perl? Probably not...
-binary_caches="$(@perl@/bin/perl -I @nix@/lib/perl5/site_perl/*/* -e 'use Nix::Config; Nix::Config::readConfig; print $Nix::Config::config{"binary-caches"};')"
-extraBuildFlags+=(--option "binary-caches" "$binary_caches")
-
-# We only need nixpkgs in the path if we don't already have a system closure to install
-if [[ -z "$closure" ]]; then
-    nixpkgs="$(readlink -f "$(nix-instantiate --find-file nixpkgs)")"
-    export NIX_PATH="nixpkgs=$nixpkgs:nixos-config=$mountPoint/$NIXOS_CONFIG"
-fi
-unset NIXOS_CONFIG
-
-# These get created in nixos-prepare-root as well, but we want to make sure they're here in case we're
-# running with --chroot. TODO: --chroot should just be split into a separate tool.
-mkdir -m 0755 -p "$mountPoint/dev" "$mountPoint/proc" "$mountPoint/sys"
-
-# Set up some bind mounts we'll want regardless of chroot or not
-mount --rbind /dev "$mountPoint/dev"
-mount --rbind /proc "$mountPoint/proc"
-mount --rbind /sys "$mountPoint/sys"
-
-# If we asked for a chroot, that means we're not actually installing anything (yeah I was confused too)
-# and we just want to run a command in the context of a $mountPoint that we're assuming has already been
-# set up by a previous nixos-install invocation. In that case we set up some remaining bind mounts and
-# exec the requested command, skipping the rest of the installation procedure.
-if [ -n "$runChroot" ]; then
-    mount -t tmpfs -o "mode=0755" none $mountPoint/run
-    rm -rf $mountPoint/var/run
-    ln -s /run $mountPoint/var/run
-    for f in /etc/resolv.conf /etc/hosts; do rm -f $mountPoint/$f; [ -f "$f" ] && cp -Lf $f $mountPoint/etc/; done
-    for f in /etc/passwd /etc/group;      do touch $mountPoint/$f; [ -f "$f" ] && mount --rbind -o ro $f $mountPoint/$f; done
-
-    if ! [ -L $mountPoint/nix/var/nix/profiles/system ]; then
-        echo "$0: installation not finished; cannot chroot into installation directory"
-        exit 1
-    fi
-    ln -s /nix/var/nix/profiles/system $mountPoint/run/current-system
-    exec chroot $mountPoint "${chrootCommand[@]}"
+if [[ ! -e $NIXOS_CONFIG && -z $system ]]; then
+    echo "configuration file $NIXOS_CONFIG doesn't exist"
+    exit 1
 fi
 
-# A place to drop temporary closures
+# A place to drop temporary stuff.
 trap "rm -rf $tmpdir" EXIT
 tmpdir="$(mktemp -d)"
 
-# Build a closure (on the host; we then copy it into the guest)
-function closure() {
-    nix-build "${extraBuildFlags[@]}" --no-out-link -E "with import <nixpkgs> {}; runCommand \"closure\" { exportReferencesGraph = [ \"x\" (buildEnv { name = \"env\"; paths = [ ($1) stdenv ]; }) ]; } \"cp x \$out\""
-}
-
-system_closure="$tmpdir/system.closure"
-# Use a FIFO for piping nix-store --export into nix-store --import, saving disk
-# I/O and space. nix-store --import is run by nixos-prepare-root.
-mkfifo $system_closure
-
-if [ -z "$closure" ]; then
-    expr="(import <nixpkgs/nixos> {}).system"
-    system_root="$(nix-build -E "$expr")"
-    system_closure="$(closure "$expr")"
-else
-    system_root=$closure
-    # Create a temporary file ending in .closure (so nixos-prepare-root knows to --import it) to transport the store closure
-    # to the filesytem we're preparing. Also delete it on exit!
-    # Run in background to avoid blocking while trying to write to the FIFO
-    # $system_closure refers to
-    nix-store --export $(nix-store -qR $closure) > $system_closure &
-fi
-
-channel_root="$(nix-env -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")"
-channel_closure="$tmpdir/channel.closure"
-nix-store --export $channel_root > $channel_closure
-
-# Populate the target root directory with the basics
-@prepare_root@/bin/nixos-prepare-root "$mountPoint" "$channel_root" "$system_root" @nixClosure@ "$system_closure" "$channel_closure"
-
-# nixos-prepare-root doesn't currently do anything with file ownership, so we set it up here instead
-chown @root_uid@:@nixbld_gid@ $mountPoint/nix/store
+sub="auto?trusted=1"
 
+# Build the system configuration in the target filesystem.
+if [[ -z $system ]]; then
+    echo "building the configuration in $NIXOS_CONFIG..."
+    outLink="$tmpdir/system"
+    nix build --out-link "$outLink" --store "$mountPoint" "${extraBuildFlags[@]}" \
+        --extra-substituters "$sub" \
+        -f '<nixpkgs/nixos>' system -I "nixos-config=$NIXOS_CONFIG"
+    system=$(readlink -f $outLink)
+fi
 
+# Set the system profile to point to the configuration. TODO: combine
+# this with the previous step once we have a nix-env replacement with
+# a progress bar.
+nix-env --store "$mountPoint" "${extraBuildFlags[@]}" \
+        --extra-substituters "$sub" \
+        -p $mountPoint/nix/var/nix/profiles/system --set "$system"
+
+# Copy the NixOS/Nixpkgs sources to the target as the initial contents
+# of the NixOS channel.
+if [[ -z $noChannelCopy ]]; then
+    if [[ -z $channelPath ]]; then
+        channelPath="$(nix-env -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")"
+    fi
+    if [[ -n $channelPath ]]; then
+        echo "copying channel..."
+        mkdir -p $mountPoint/nix/var/nix/profiles/per-user/root
+        nix-env --store "$mountPoint" "${extraBuildFlags[@]}" --extra-substituters "$sub" \
+                -p $mountPoint/nix/var/nix/profiles/per-user/root/channels --set "$channelPath" --quiet
+        install -m 0700 -d $mountPoint/root/.nix-defexpr
+        ln -sfn /nix/var/nix/profiles/per-user/root/channels $mountPoint/root/.nix-defexpr/channels
+    fi
+fi
 
-# Grub needs an mtab.
-ln -sfn /proc/mounts $mountPoint/etc/mtab
+# Mark the target as a NixOS installation, otherwise switch-to-configuration will chicken out.
+mkdir -m 0755 -p "$mountPoint/etc"
+touch "$mountPoint/etc/NIXOS"
 
 # Switch to the new system configuration.  This will install Grub with
 # a menu default pointing at the kernel/initrd/etc of the new
 # configuration.
-echo "finalising the installation..."
-if [ -z "$noBootLoader" ]; then
-  NIXOS_INSTALL_BOOTLOADER=1 chroot $mountPoint \
-      /nix/var/nix/profiles/system/bin/switch-to-configuration boot
+if [[ -z $noBootLoader ]]; then
+    echo "installing the boot loader..."
+    # Grub needs an mtab.
+    ln -sfn /proc/mounts $mountPoint/etc/mtab
+    NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root "$mountPoint" -- /run/current-system/bin/switch-to-configuration boot
 fi
 
-# Run the activation script.
-chroot $mountPoint /nix/var/nix/profiles/system/activate
-
-
-# Ask the user to set a root password.
-if [ -z "$noRootPasswd" ] && chroot $mountPoint [ -x /run/wrappers/bin/passwd ] && [ -t 0 ]; then
-    echo "setting root password..."
-    chroot $mountPoint /run/wrappers/bin/passwd
+# Ask the user to set a root password, but only if the passwd command
+# exists (i.e. when mutable user accounts are enabled).
+if [[ -z $noRootPasswd ]] && [ -t 0 ]; then
+    nixos-enter --root "$mountPoint" -c '[[ -e /nix/var/nix/profiles/system/sw/bin/passwd ]] && echo "setting root password..." && /nix/var/nix/profiles/system/sw/bin/passwd'
 fi
 
-
 echo "installation finished!"
diff --git a/nixos/modules/installer/tools/nixos-prepare-root.sh b/nixos/modules/installer/tools/nixos-prepare-root.sh
deleted file mode 100644
index ed5af234fec9..000000000000
--- a/nixos/modules/installer/tools/nixos-prepare-root.sh
+++ /dev/null
@@ -1,104 +0,0 @@
-#! @shell@
-
-# This script's goal is to perform all "static" setup of a filesystem structure from pre-built store paths. Everything
-# in here should run in a non-root context and inside a Nix builder. It's designed primarily to be called from image-
-# building scripts and from nixos-install, but because it makes very few assumptions about the context in which it runs,
-# it could be useful in other contexts as well.
-#
-# Current behavior:
-#  - set up basic filesystem structure
-#  - make Nix store etc.
-#  - copy Nix, system, channel, and misceallaneous closures to target Nix store
-#  - register validity of all paths in the target store
-#  - set up channel and system profiles
-
-# Ensure a consistent umask.
-umask 0022
-
-set -e
-
-mountPoint="$1"
-channel="$2"
-system="$3"
-shift 3
-closures="$@"
-
-PATH="@coreutils@/bin:@nix@/bin:@perl@/bin:@utillinux@/bin:@rsync@/bin"
-
-if ! test -e "$mountPoint"; then
-    echo "mount point $mountPoint doesn't exist"
-    exit 1
-fi
-
-# Create a few of the standard directories in the target root directory.
-install -m 0755 -d $mountPoint/dev $mountPoint/proc $mountPoint/sys $mountPoint/etc $mountPoint/run $mountPoint/home
-install -m 01777 -d $mountPoint/tmp
-install -m 0755 -d $mountPoint/tmp/root
-install -m 0755 -d $mountPoint/var
-install -m 0700 -d $mountPoint/root
-
-ln -sf /run $mountPoint/var/run
-
-# Create the necessary Nix directories on the target device
-install -m 0755 -d \
-    $mountPoint/nix/var/nix/gcroots \
-    $mountPoint/nix/var/nix/temproots \
-    $mountPoint/nix/var/nix/userpool \
-    $mountPoint/nix/var/nix/profiles \
-    $mountPoint/nix/var/nix/db \
-    $mountPoint/nix/var/log/nix/drvs
-
-install -m 1775 -d $mountPoint/nix/store
-
-# All Nix operations below should operate on our target store, not /nix/store.
-# N.B: this relies on Nix 1.12 or higher
-export NIX_REMOTE=local?root=$mountPoint
-
-# Copy our closures to the Nix store on the target mount point, unless they're already there.
-for i in $closures; do
-    # We support closures both in the format produced by `nix-store --export` and by `exportReferencesGraph`,
-    # mostly because there doesn't seem to be a single format that can be produced outside of a nix build and
-    # inside one. See https://github.com/NixOS/nix/issues/1242 for more discussion.
-    if [[ "$i" =~ \.closure$ ]]; then
-        echo "importing serialized closure $i to $mountPoint..."
-        nix-store --import < $i
-    else
-        # There has to be a better way to do this, right?
-        echo "copying closure $i to $mountPoint..."
-        for j in $(perl @pathsFromGraph@ $i); do
-            echo "  $j... "
-            rsync -a $j $mountPoint/nix/store/
-        done
-
-        nix-store --option build-users-group root --register-validity < $i
-    fi
-done
-
-# Create the required /bin/sh symlink; otherwise lots of things
-# (notably the system() function) won't work.
-if [ ! -x $mountPoint/@shell@ ]; then
-    echo "Error: @shell@ wasn't included in the closure" >&2
-    exit 1
-fi
-install -m 0755 -d $mountPoint/bin
-ln -sf @shell@ $mountPoint/bin/sh
-
-echo "setting the system closure to '$system'..."
-nix-env "${extraBuildFlags[@]}" -p $mountPoint/nix/var/nix/profiles/system --set "$system"
-
-ln -sfn /nix/var/nix/profiles/system $mountPoint/run/current-system
-
-# Copy the NixOS/Nixpkgs sources to the target as the initial contents of the NixOS channel.
-install -m 0755 -d $mountPoint/nix/var/nix/profiles
-install -m 1777 -d $mountPoint/nix/var/nix/profiles/per-user
-install -m 0755 -d $mountPoint/nix/var/nix/profiles/per-user/root
-
-if [ -z "$noChannelCopy" ] && [ -n "$channel" ]; then
-    echo "copying channel..."
-    nix-env --option build-use-substitutes false "${extraBuildFlags[@]}" -p $mountPoint/nix/var/nix/profiles/per-user/root/channels --set "$channel" --quiet
-fi
-install -m 0700 -d $mountPoint/root/.nix-defexpr
-ln -sfn /nix/var/nix/profiles/per-user/root/channels $mountPoint/root/.nix-defexpr/channels
-
-# Mark the target as a NixOS installation, otherwise switch-to-configuration will chicken out.
-touch $mountPoint/etc/NIXOS
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 9ede74a54cd7..2af73519bc52 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -382,6 +382,6 @@ fi
 if [ "$action" = build-vm ]; then
     cat >&2 <<EOF
 
-Done.  The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm).
+Done.  The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm)
 EOF
 fi
diff --git a/nixos/modules/installer/tools/nixos-version.sh b/nixos/modules/installer/tools/nixos-version.sh
index 77a1b458a342..190c49a33ec6 100644
--- a/nixos/modules/installer/tools/nixos-version.sh
+++ b/nixos/modules/installer/tools/nixos-version.sh
@@ -6,9 +6,9 @@ case "$1" in
     exit 1
     ;;
   --hash|--revision)
-    echo "@nixosRevision@"
+    echo "@revision@"
     ;;
   *)
-    echo "@nixosVersion@ (@nixosCodeName@)"
+    echo "@version@ (@codeName@)"
     ;;
 esac
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index a3bae78c0ffc..beac9e29d59c 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -1,7 +1,9 @@
 # This module generates nixos-install, nixos-rebuild,
 # nixos-generate-config, etc.
 
-{ config, pkgs, modulesPath, ... }:
+{ config, lib, pkgs, modulesPath, ... }:
+
+with lib;
 
 let
   cfg = config.installer;
@@ -16,28 +18,11 @@ let
     src = ./nixos-build-vms/nixos-build-vms.sh;
   };
 
-  nixos-prepare-root = makeProg {
-    name = "nixos-prepare-root";
-    src = ./nixos-prepare-root.sh;
-
-    nix = pkgs.nixUnstable;
-    inherit (pkgs) perl pathsFromGraph rsync utillinux coreutils;
-  };
-
   nixos-install = makeProg {
     name = "nixos-install";
     src = ./nixos-install.sh;
-
-    inherit (pkgs) perl pathsFromGraph rsync;
     nix = config.nix.package.out;
-    cacert = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
-    root_uid = config.ids.uids.root;
-    nixbld_gid = config.ids.gids.nixbld;
-    prepare_root = nixos-prepare-root;
-
-    nixClosure = pkgs.runCommand "closure"
-      { exportReferencesGraph = ["refs" config.nix.package.out]; }
-      "cp refs $out";
+    path = makeBinPath [ nixos-enter ];
   };
 
   nixos-rebuild =
@@ -55,7 +40,7 @@ let
     src = ./nixos-generate-config.pl;
     path = [ pkgs.btrfs-progs ];
     perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
-    inherit (config.system) nixosRelease;
+    inherit (config.system.nixos) release;
   };
 
   nixos-option = makeProg {
@@ -66,7 +51,12 @@ let
   nixos-version = makeProg {
     name = "nixos-version";
     src = ./nixos-version.sh;
-    inherit (config.system) nixosVersion nixosCodeName nixosRevision;
+    inherit (config.system.nixos) version codeName revision;
+  };
+
+  nixos-enter = makeProg {
+    name = "nixos-enter";
+    src = ./nixos-enter.sh;
   };
 
 in
@@ -77,16 +67,16 @@ in
 
     environment.systemPackages =
       [ nixos-build-vms
-        nixos-prepare-root
         nixos-install
         nixos-rebuild
         nixos-generate-config
         nixos-option
         nixos-version
+        nixos-enter
       ];
 
     system.build = {
-      inherit nixos-install nixos-prepare-root nixos-generate-config nixos-option nixos-rebuild;
+      inherit nixos-install nixos-prepare-root nixos-generate-config nixos-option nixos-rebuild nixos-enter;
     };
 
   };
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 5316cfce906b..13a0d7f4f6ee 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -19,4 +19,6 @@ with lib;
   # Add some more video drivers to give X11 a shot at working in
   # VMware and QEMU.
   services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" "modesetting" ];
+
+  powerManagement.enable = false;
 }
diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
new file mode 100644
index 000000000000..cea8981370bb
--- /dev/null
+++ b/nixos/modules/misc/documentation.nix
@@ -0,0 +1,77 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.documentation; in
+
+{
+
+  options = {
+
+    documentation = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install documentation of packages from
+          <option>environment.systemPackages</option> into the generated system path.
+        '';
+      };
+
+      man.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install manual pages and the <command>man</command> command.
+          This also includes "man" outputs.
+        '';
+      };
+
+      doc.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install documentation distributed in packages' <literal>/share/doc</literal>.
+          Usually plain text and/or HTML.
+          This also includes "doc" outputs.
+        '';
+      };
+
+      info.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install info pages and the <command>info</command> command.
+          This also includes "info" outputs.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable (mkMerge [
+
+    (mkIf cfg.man.enable {
+      environment.systemPackages = [ pkgs.man-db ];
+      environment.pathsToLink = [ "/share/man" ];
+      environment.extraOutputsToInstall = [ "man" ];
+    })
+
+    (mkIf cfg.doc.enable {
+      # TODO(@oxij): put it here and remove from profiles?
+      # environment.systemPackages = [ pkgs.w3m ]; # w3m-nox?
+      environment.pathsToLink = [ "/share/doc" ];
+      environment.extraOutputsToInstall = [ "doc" ];
+    })
+
+    (mkIf cfg.info.enable {
+      environment.systemPackages = [ pkgs.texinfoInteractive ];
+      environment.pathsToLink = [ "/share/info" ];
+      environment.extraOutputsToInstall = [ "info" ];
+    })
+
+  ]);
+
+}
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 7d0cbf6aad02..ab3cbcab0646 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -56,7 +56,7 @@
       #dialout = 27; # unused
       polkituser = 28;
       #utmp = 29; # unused
-      ddclient = 30;
+      # ddclient = 30; # converted to DynamicUser = true
       davfs2 = 31;
       #disnix = 33; # unused
       osgi = 34;
@@ -65,7 +65,7 @@
       foldingathome = 37;
       sabnzbd = 38;
       #kdm = 39; # dropped in 17.03
-      ghostone = 40;
+      #ghostone = 40; # dropped in 18.03
       git = 41;
       fourstore = 42;
       fourstorehttp = 43;
@@ -106,7 +106,7 @@
       freenet = 79;
       ircd = 80;
       bacula = 81;
-      almir = 82;
+      #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08
       deluge = 83;
       mysql = 84;
       rabbitmq = 85;
@@ -197,10 +197,10 @@
       #input = 174; # unused
       sddm = 175;
       tss = 176;
-      memcached = 177;
+      #memcached = 177; removed 2018-01-03
       ntp = 179;
       zabbix = 180;
-      redis = 181;
+      #redis = 181; removed 2018-01-03
       unifi = 183;
       uptimed = 184;
       zope2 = 185;
@@ -233,7 +233,7 @@
       calibre-server = 213;
       heapster = 214;
       bepasty = 215;
-      pumpio = 216;
+      # pumpio = 216; # unused, removed 2018-02-24
       nm-openvpn = 217;
       mathics = 218;
       ejabberd = 219;
@@ -281,8 +281,8 @@
       stanchion = 262;
       riak-cs = 263;
       infinoted = 264;
-      keystone = 265;
-      glance = 266;
+      # keystone = 265; # unused, removed 2017-12-13
+      # glance = 266; # unused, removed 2017-12-13
       couchpotato = 267;
       gogs = 268;
       pdns-recursor = 269;
@@ -300,6 +300,12 @@
       kanboard = 281;
       pykms = 282;
       kodi = 283;
+      restya-board = 284;
+      mighttpd2 = 285;
+      hass = 286;
+      monero = 287;
+      ceph = 288;
+      duplicati = 289;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -338,7 +344,7 @@
       dialout = 27;
       #polkituser = 28; # currently unused, polkitd doesn't need a group
       utmp = 29;
-      ddclient = 30;
+      # ddclient = 30; # converted to DynamicUser = true
       davfs2 = 31;
       disnix = 33;
       osgi = 34;
@@ -347,7 +353,7 @@
       #foldingathome = 37; # unused
       #sabnzd = 38; # unused
       #kdm = 39; # unused, even before 17.03
-      ghostone = 40;
+      #ghostone = 40; # dropped in 18.03
       git = 41;
       fourstore = 42;
       fourstorehttp = 43;
@@ -388,7 +394,7 @@
       freenet = 79;
       ircd = 80;
       bacula = 81;
-      almir = 82;
+      #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08
       deluge = 83;
       mysql = 84;
       rabbitmq = 85;
@@ -474,10 +480,10 @@
       input = 174;
       sddm = 175;
       tss = 176;
-      #memcached = 177; # unused
+      #memcached = 177; # unused, removed 2018-01-03
       #ntp = 179; # unused
       #zabbix = 180; # unused
-      #redis = 181; # unused
+      #redis = 181; # unused, removed 2018-01-03
       #unifi = 183; # unused
       #uptimed = 184; # unused
       #zope2 = 185; # unused
@@ -509,7 +515,7 @@
       xtreemfs = 212;
       calibre-server = 213;
       bepasty = 215;
-      pumpio = 216;
+      # pumpio = 216; # unused, removed 2018-02-24
       nm-openvpn = 217;
       mathics = 218;
       ejabberd = 219;
@@ -550,8 +556,8 @@
       stanchion = 262;
       riak-cs = 263;
       infinoted = 264;
-      keystone = 265;
-      glance = 266;
+      # keystone = 265; # unused, removed 2017-12-13
+      # glance = 266; # unused, removed 2017-12-13
       couchpotato = 267;
       gogs = 268;
       kresd = 270;
@@ -568,6 +574,12 @@
       kanboard = 281;
       pykms = 282;
       kodi = 283;
+      restya-board = 284;
+      mighttpd2 = 285;
+      hass = 286;
+      monero = 287;
+      ceph = 288;
+      duplicati = 289;
 
       # 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/misc/label.nix b/nixos/modules/misc/label.nix
new file mode 100644
index 000000000000..8e5e57b3b83b
--- /dev/null
+++ b/nixos/modules/misc/label.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.system.nixos;
+in
+
+{
+
+  options.system = {
+
+    nixos.label = mkOption {
+      type = types.str;
+      description = ''
+        NixOS version name to be used in the names of generated
+        outputs and boot labels.
+
+        If you ever wanted to influence the labels in your GRUB menu,
+        this is the option for you.
+
+        The default is <option>system.nixos.tags</option> separated by
+        "-" + "-" + <envar>NIXOS_LABEL_VERSION</envar> environment
+        variable (defaults to the value of
+        <option>system.nixos.version</option>).
+
+        Can be overriden by setting <envar>NIXOS_LABEL</envar>.
+
+        Useful for not loosing track of configurations built from different
+        nixos branches/revisions, e.g.:
+
+        <screen>
+        #!/bin/sh
+        today=`date +%Y%m%d`
+        branch=`(cd nixpkgs ; git branch 2>/dev/null | sed -n '/^\* / { s|^\* ||; p; }')`
+        revision=`(cd nixpkgs ; git rev-parse HEAD)`
+        export NIXOS_LABEL_VERSION="$today.$branch-''${revision:0:7}"
+        nixos-rebuild switch</screen>
+      '';
+    };
+
+    nixos.tags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "with-xen" ];
+      description = ''
+        Strings to prefix to the default
+        <option>system.nixos.label</option>.
+
+        Useful for not loosing track of configurations built with
+        different options, e.g.:
+
+        <screen>
+        {
+          system.nixos.tags = [ "with-xen" ];
+          virtualisation.xen.enable = true;
+        }
+        </screen>
+      '';
+    };
+
+  };
+
+  config = {
+    # This is set here rather than up there so that changing it would
+    # not rebuild the manual
+    system.nixos.label = mkDefault (maybeEnv "NIXOS_LABEL"
+                                             (concatStringsSep "-" ((sort (x: y: x < y) cfg.tags)
+                                              ++ [ (maybeEnv "NIXOS_LABEL_VERSION" cfg.version) ])));
+  };
+
+}
diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix
index 51953d1110c4..ce5765cf1978 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -97,7 +97,7 @@ in {
         Whether not to index bind mounts
       '';
     };
-    
+
   };
 
   config = mkIf cfg.enable {
@@ -133,13 +133,26 @@ in {
     systemd.services.update-locatedb =
       { description = "Update Locate Database";
         path = mkIf (!isMLocate) [ pkgs.su ];
+
+        # mlocate's updatedb takes flags via a configuration file or
+        # on the command line, but not by environment variable.
         script =
+          if isMLocate
+          then let toFlags = x: optional (cfg.${x} != [])
+                                         "--${lib.toLower x} '${concatStringsSep " " cfg.${x}}'";
+                   args = concatLists (map toFlags ["pruneFS" "pruneNames" "prunePaths"]);
+               in ''
+            exec ${cfg.locate}/bin/updatedb \
+              --output ${toString cfg.output} ${concatStringsSep " " args} \
+              --prune-bind-mounts ${if cfg.pruneBindMounts then "yes" else "no"} \
+              ${concatStringsSep " " cfg.extraFlags}
           ''
+          else ''
             exec ${cfg.locate}/bin/updatedb \
               ${optionalString (cfg.localuser != null && ! isMLocate) ''--localuser=${cfg.localuser}''} \
               --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
           '';
-        environment = {
+        environment = optionalAttrs (!isMLocate) {
           PRUNEFS = concatStringsSep " " cfg.pruneFS;
           PRUNEPATHS = concatStringsSep " " cfg.prunePaths;
           PRUNENAMES = concatStringsSep " " cfg.pruneNames;
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 1793c1447d60..b8a55a24394e 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -3,11 +3,13 @@
 with lib;
 
 let
+  cfg = config.nixpkgs;
+
   isConfig = x:
-    builtins.isAttrs x || builtins.isFunction x;
+    builtins.isAttrs x || lib.isFunction x;
 
   optCall = f: x:
-    if builtins.isFunction f
+    if lib.isFunction f
     then f x
     else f;
 
@@ -38,27 +40,72 @@ let
   overlayType = mkOptionType {
     name = "nixpkgs-overlay";
     description = "nixpkgs overlay";
-    check = builtins.isFunction;
+    check = lib.isFunction;
     merge = lib.mergeOneOption;
   };
 
-  _pkgs = import ../../.. config.nixpkgs;
+  pkgsType = mkOptionType {
+    name = "nixpkgs";
+    description = "An evaluation of Nixpkgs; the top level attribute set of packages";
+    check = builtins.isAttrs;
+  };
 
 in
 
 {
   options.nixpkgs = {
+
+    pkgs = mkOption {
+      defaultText = literalExample
+        ''import "''${nixos}/.." {
+            inherit (config.nixpkgs) config overlays localSystem crossSystem;
+          }
+        '';
+      default = import ../../.. {
+        localSystem = { inherit (cfg) system; } // cfg.localSystem;
+        inherit (cfg) config overlays crossSystem;
+      };
+      type = pkgsType;
+      example = literalExample ''import <nixpkgs> {}'';
+      description = ''
+        This is the evaluation of Nixpkgs that will be provided to
+        all NixOS modules. Defining this option has the effect of
+        ignoring the other options that would otherwise be used to
+        evaluate Nixpkgs, because those are arguments to the default
+        value. The default value imports the Nixpkgs source files
+        relative to the location of this NixOS module, because
+        NixOS and Nixpkgs are distributed together for consistency,
+        so the <code>nixos</code> in the default value is in fact a
+        relative path. The <code>config</code>, <code>overlays</code>,
+        <code>localSystem</code>, and <code>crossSystem</code> come
+        from this option's siblings.
+
+        This option can be used by applications like NixOps to increase
+        the performance of evaluation, or to create packages that depend
+        on a container that should be built with the exact same evaluation
+        of Nixpkgs, for example. Applications like this should set
+        their default value using <code>lib.mkDefault</code>, so
+        user-provided configuration can override it without using
+        <code>lib</code>.
+
+        Note that using a distinct version of Nixpkgs with NixOS may
+        be an unexpected source of problems. Use this option with care.
+      '';
+    };
+
     config = mkOption {
       default = {};
       example = literalExample
         ''
-          { firefox.enableGeckoMediaPlayer = true; }
+          { allowBroken = true; allowUnfree = true; }
         '';
       type = configType;
       description = ''
         The configuration of the Nix Packages collection.  (For
         details, see the Nixpkgs documentation.)  It allows you to set
         package configuration options.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
 
@@ -69,7 +116,6 @@ in
           [ (self: super: {
               openssh = super.openssh.override {
                 hpnSupport = true;
-                withKerberos = true;
                 kerberos = self.libkrb5;
               };
             };
@@ -83,6 +129,47 @@ in
         takes as an argument the <emphasis>original</emphasis> Nixpkgs.
         The first argument should be used for finding dependencies, and
         the second should be used for overriding recipes.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
+    localSystem = mkOption {
+      type = types.attrs; # TODO utilize lib.systems.parsedPlatform
+      default = { system = builtins.currentSystem; };
+      example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+      defaultText = literalExample
+        ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
+      description = ''
+        Specifies the platform on which NixOS should be built. When
+        <code>nixpkgs.crossSystem</code> is unset, it also specifies
+        the platform <emphasis>for</emphasis> which NixOS should be
+        built.  If this option is unset, it defaults to the platform
+        type of the machine where evaluation happens. Specifying this
+        option is useful when doing distributed multi-platform
+        deployment, or when building virtual machines. See its
+        description in the Nixpkgs manual for more details.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
+    crossSystem = mkOption {
+      type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
+      default = null;
+      example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+      defaultText = literalExample
+        ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
+      description = ''
+        Specifies the platform for which NixOS should be
+        built. Specify this only if it is different from
+        <code>nixpkgs.localSystem</code>, the platform
+        <emphasis>on</emphasis> which NixOS should be built. In other
+        words, specify this to cross-compile NixOS. Otherwise it
+        should be set as null, the default. See its description in the
+        Nixpkgs manual for more details.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
 
@@ -90,18 +177,30 @@ in
       type = types.str;
       example = "i686-linux";
       description = ''
-        Specifies the Nix platform type for which NixOS should be built.
-        If unset, it defaults to the platform type of your host system.
-        Specifying this option is useful when doing distributed
-        multi-platform deployment, or when building virtual machines.
+        Specifies the Nix platform type on which NixOS should be built.
+        It is better to specify <code>nixpkgs.localSystem</code> instead.
+        <programlisting>
+        {
+          nixpkgs.system = ..;
+        }
+        </programlisting>
+        is the same as
+        <programlisting>
+        {
+          nixpkgs.localSystem.system = ..;
+        }
+        </programlisting>
+        See <code>nixpkgs.localSystem</code> for more information.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
   };
 
   config = {
     _module.args = {
-      pkgs = _pkgs;
-      pkgs_i686 = _pkgs.pkgsi686Linux;
+      pkgs = cfg.pkgs;
+      pkgs_i686 = cfg.pkgs.pkgsi686Linux;
     };
   };
 }
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 48cde2ebbc8a..7519d9176982 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -3,7 +3,7 @@
 with lib;
 
 let
-  cfg = config.system;
+  cfg = config.system.nixos;
 
   releaseFile  = "${toString pkgs.path}/.version";
   suffixFile   = "${toString pkgs.path}/.version-suffix";
@@ -16,51 +16,42 @@ in
 
   options.system = {
 
-    stateVersion = mkOption {
+    # XXX: Reintroduce old options to make nixops before 1.6 able to evaluate configurations
+    # XXX: Remove after nixops has been bumped to a compatible version
+    nixosVersion = mkOption {
+      readOnly = true;
+      internal = true;
       type = types.str;
-      default = cfg.nixosRelease;
-      description = ''
-        Every once in a while, a new NixOS release may change
-        configuration defaults in a way incompatible with stateful
-        data. For instance, if the default version of PostgreSQL
-        changes, the new version will probably be unable to read your
-        existing databases. To prevent such breakage, you can set the
-        value of this option to the NixOS release with which you want
-        to be compatible. The effect is that NixOS will option
-        defaults corresponding to the specified release (such as using
-        an older version of PostgreSQL).
-      '';
+      default = config.system.nixos.version;
     };
-
-    nixosLabel = mkOption {
+    nixosVersionSuffix = mkOption {
+      readOnly = true;
+      internal = true;
       type = types.str;
-      description = ''
-        Label to be used in the names of generated outputs and boot
-        labels.
-      '';
+      default = config.system.nixos.versionSuffix;
     };
 
-    nixosVersion = mkOption {
+    nixos.version = mkOption {
       internal = true;
       type = types.str;
       description = "The full NixOS version (e.g. <literal>16.03.1160.f2d4ee1</literal>).";
     };
 
-    nixosRelease = mkOption {
+    nixos.release = mkOption {
       readOnly = true;
       type = types.str;
       default = fileContents releaseFile;
       description = "The NixOS release (e.g. <literal>16.03</literal>).";
     };
 
-    nixosVersionSuffix = mkOption {
+    nixos.versionSuffix = mkOption {
       internal = true;
       type = types.str;
       default = if pathExists suffixFile then fileContents suffixFile else "pre-git";
       description = "The NixOS version suffix (e.g. <literal>1160.f2d4ee1</literal>).";
     };
 
-    nixosRevision = mkOption {
+    nixos.revision = mkOption {
       internal = true;
       type = types.str;
       default = if pathIsDirectory gitRepo then commitIdFromGitRepo gitRepo
@@ -69,12 +60,28 @@ in
       description = "The Git revision from which this NixOS configuration was built.";
     };
 
-    nixosCodeName = mkOption {
+    nixos.codeName = mkOption {
       readOnly = true;
       type = types.str;
       description = "The NixOS release code name (e.g. <literal>Emu</literal>).";
     };
 
+    stateVersion = mkOption {
+      type = types.str;
+      default = cfg.release;
+      description = ''
+        Every once in a while, a new NixOS release may change
+        configuration defaults in a way incompatible with stateful
+        data. For instance, if the default version of PostgreSQL
+        changes, the new version will probably be unable to read your
+        existing databases. To prevent such breakage, you can set the
+        value of this option to the NixOS release with which you want
+        to be compatible. The effect is that NixOS will option
+        defaults corresponding to the specified release (such as using
+        an older version of PostgreSQL).
+      '';
+    };
+
     defaultChannel = mkOption {
       internal = true;
       type = types.str;
@@ -86,16 +93,15 @@ in
 
   config = {
 
-    system = {
+    system.nixos = {
       # These defaults are set here rather than up there so that
       # changing them would not rebuild the manual
-      nixosLabel   = mkDefault cfg.nixosVersion;
-      nixosVersion = mkDefault (cfg.nixosRelease + cfg.nixosVersionSuffix);
-      nixosRevision      = mkIf (pathIsDirectory gitRepo) (mkDefault            gitCommitId);
-      nixosVersionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
+      version = mkDefault (cfg.release + cfg.versionSuffix);
+      revision      = mkIf (pathIsDirectory gitRepo) (mkDefault            gitCommitId);
+      versionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
 
-      # Note: code names must only increase in alphabetical order.
-      nixosCodeName = "Impala";
+      # Note: the first letter is bumped on every release.  It's an animal.
+      codeName = "Jellyfish";
     };
 
     # Generate /etc/os-release.  See
@@ -105,10 +111,10 @@ in
       ''
         NAME=NixOS
         ID=nixos
-        VERSION="${config.system.nixosVersion} (${config.system.nixosCodeName})"
-        VERSION_CODENAME=${toLower config.system.nixosCodeName}
-        VERSION_ID="${config.system.nixosVersion}"
-        PRETTY_NAME="NixOS ${config.system.nixosVersion} (${config.system.nixosCodeName})"
+        VERSION="${cfg.version} (${cfg.codeName})"
+        VERSION_CODENAME=${toLower cfg.codeName}
+        VERSION_ID="${cfg.version}"
+        PRETTY_NAME="NixOS ${cfg.version} (${cfg.codeName})"
         HOME_URL="https://nixos.org/"
         SUPPORT_URL="https://nixos.org/nixos/support.html"
         BUG_REPORT_URL="https://github.com/NixOS/nixpkgs/issues"
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index d63606489649..74c8c7bbc820 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -31,6 +31,7 @@
   ./hardware/ckb.nix
   ./hardware/cpu/amd-microcode.nix
   ./hardware/cpu/intel-microcode.nix
+  ./hardware/digitalbitbox.nix
   ./hardware/sensor/iio.nix
   ./hardware/ksm.nix
   ./hardware/mcelog.nix
@@ -40,6 +41,7 @@
   ./hardware/pcmcia.nix
   ./hardware/raid/hpsa.nix
   ./hardware/usb-wwan.nix
+  ./hardware/onlykey.nix
   ./hardware/video/amdgpu.nix
   ./hardware/video/amdgpu-pro.nix
   ./hardware/video/ati.nix
@@ -57,9 +59,11 @@
   ./installer/tools/tools.nix
   ./misc/assertions.nix
   ./misc/crashdump.nix
+  ./misc/documentation.nix
   ./misc/extra-arguments.nix
   ./misc/ids.nix
   ./misc/lib.nix
+  ./misc/label.nix
   ./misc/locate.nix
   ./misc/meta.nix
   ./misc/nixpkgs.nix
@@ -71,44 +75,55 @@
   ./programs/bcc.nix
   ./programs/blcr.nix
   ./programs/browserpass.nix
+  ./programs/ccache.nix
   ./programs/cdemu.nix
   ./programs/chromium.nix
   ./programs/command-not-found/command-not-found.nix
+  ./programs/criu.nix
   ./programs/dconf.nix
+  ./programs/digitalbitbox/default.nix
   ./programs/environment.nix
   ./programs/fish.nix
   ./programs/freetds.nix
   ./programs/gnupg.nix
   ./programs/gphoto2.nix
-  ./programs/info.nix
+  ./programs/iftop.nix
   ./programs/java.nix
   ./programs/kbdlight.nix
+  ./programs/less.nix
   ./programs/light.nix
-  ./programs/man.nix
   ./programs/mosh.nix
   ./programs/mtr.nix
   ./programs/nano.nix
   ./programs/npm.nix
   ./programs/oblogout.nix
+  ./programs/plotinus.nix
   ./programs/qt5ct.nix
+  ./programs/rootston.nix
   ./programs/screen.nix
   ./programs/slock.nix
   ./programs/shadow.nix
   ./programs/shell.nix
   ./programs/spacefm.nix
+  ./programs/singularity.nix
   ./programs/ssh.nix
   ./programs/ssmtp.nix
   ./programs/sysdig.nix
+  ./programs/systemtap.nix
   ./programs/sway.nix
   ./programs/thefuck.nix
   ./programs/tmux.nix
+  ./programs/udevil.nix
   ./programs/venus.nix
   ./programs/vim.nix
+  ./programs/way-cooler.nix
   ./programs/wireshark.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
+  ./programs/yabar.nix
   ./programs/zsh/oh-my-zsh.nix
   ./programs/zsh/zsh.nix
+  ./programs/zsh/zsh-autoenv.nix
   ./programs/zsh/zsh-syntax-highlighting.nix
   ./rename.nix
   ./security/acme.nix
@@ -144,11 +159,14 @@
   ./services/audio/slimserver.nix
   ./services/audio/squeezelite.nix
   ./services/audio/ympd.nix
-  ./services/backup/almir.nix
   ./services/backup/bacula.nix
+  ./services/backup/borgbackup.nix
+  ./services/backup/duplicati.nix
   ./services/backup/crashplan.nix
+  ./services/backup/crashplan-small-business.nix
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
+  ./services/backup/restic.nix
   ./services/backup/rsnapshot.nix
   ./services/backup/tarsnap.nix
   ./services/backup/znapzend.nix
@@ -196,7 +214,9 @@
   ./services/desktops/dleyna-renderer.nix
   ./services/desktops/dleyna-server.nix
   ./services/desktops/geoclue2.nix
+  ./services/desktops/pipewire.nix
   ./services/desktops/gnome3/at-spi2-core.nix
+  ./services/desktops/gnome3/chrome-gnome-shell.nix
   ./services/desktops/gnome3/evolution-data-server.nix
   ./services/desktops/gnome3/gnome-disks.nix
   ./services/desktops/gnome3/gnome-documents.nix
@@ -210,22 +230,22 @@
   ./services/desktops/gnome3/seahorse.nix
   ./services/desktops/gnome3/sushi.nix
   ./services/desktops/gnome3/tracker.nix
+  ./services/desktops/gnome3/tracker-miners.nix
   ./services/desktops/profile-sync-daemon.nix
   ./services/desktops/telepathy.nix
   ./services/development/hoogle.nix
   ./services/editors/emacs.nix
   ./services/editors/infinoted.nix
   ./services/games/factorio.nix
-  ./services/games/ghost-one.nix
   ./services/games/minecraft-server.nix
   ./services/games/minetest-server.nix
   ./services/games/terraria.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
-  ./services/hardware/amd-hybrid-graphics.nix
   ./services/hardware/bluetooth.nix
   ./services/hardware/brltty.nix
   ./services/hardware/freefall.nix
+  ./services/hardware/fwupd.nix
   ./services/hardware/illum.nix
   ./services/hardware/interception-tools.nix
   ./services/hardware/irqbalance.nix
@@ -233,13 +253,16 @@
   ./services/hardware/pcscd.nix
   ./services/hardware/pommed.nix
   ./services/hardware/sane.nix
+  ./services/hardware/sane_extra_backends/brscan4.nix
   ./services/hardware/tcsd.nix
   ./services/hardware/tlp.nix
   ./services/hardware/thinkfan.nix
   ./services/hardware/trezord.nix
+  ./services/hardware/u2f.nix
   ./services/hardware/udev.nix
   ./services/hardware/udisks2.nix
   ./services/hardware/upower.nix
+  ./services/hardware/usbmuxd.nix
   ./services/hardware/thermald.nix
   ./services/logging/SystemdJournal2Gelf.nix
   ./services/logging/awstats.nix
@@ -255,6 +278,8 @@
   ./services/logging/rsyslogd.nix
   ./services/logging/syslog-ng.nix
   ./services/logging/syslogd.nix
+  ./services/mail/clamsmtp.nix
+  ./services/mail/dkimproxy-out.nix
   ./services/mail/dovecot.nix
   ./services/mail/dspam.nix
   ./services/mail/exim.nix
@@ -301,11 +326,13 @@
   ./services/misc/geoip-updater.nix
   ./services/misc/gitea.nix
   #./services/misc/gitit.nix
-  ./services/misc/gitlab.nix
+  #./services/misc/gitlab.nix
   ./services/misc/gitolite.nix
+  ./services/misc/gitweb.nix
   ./services/misc/gogs.nix
   ./services/misc/gollum.nix
   ./services/misc/gpsd.nix
+  ./services/misc/home-assistant.nix
   ./services/misc/ihaskell.nix
   ./services/misc/irkerd.nix
   ./services/misc/jackett.nix
@@ -324,8 +351,10 @@
   ./services/misc/nix-optimise.nix
   ./services/misc/nixos-manual.nix
   ./services/misc/nix-ssh-serve.nix
+  ./services/misc/novacomd.nix
   ./services/misc/nzbget.nix
   ./services/misc/octoprint.nix
+  ./services/misc/osrm.nix
   ./services/misc/packagekit.nix
   ./services/misc/parsoid.nix
   ./services/misc/phd.nix
@@ -337,6 +366,8 @@
   ./services/misc/rippled.nix
   ./services/misc/ripple-data-api.nix
   ./services/misc/rogue.nix
+  ./services/misc/serviio.nix
+  ./services/misc/safeeyes.nix
   ./services/misc/siproxd.nix
   ./services/misc/snapper.nix
   ./services/misc/sonarr.nix
@@ -350,6 +381,7 @@
   ./services/misc/taskserver
   ./services/misc/tzupdate.nix
   ./services/misc/uhub.nix
+  ./services/misc/xmr-stak.nix
   ./services/misc/zookeeper.nix
   ./services/monitoring/apcupsd.nix
   ./services/monitoring/arbtt.nix
@@ -371,16 +403,7 @@
   ./services/monitoring/osquery.nix
   ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/alertmanager.nix
-  ./services/monitoring/prometheus/blackbox-exporter.nix
-  ./services/monitoring/prometheus/collectd-exporter.nix
-  ./services/monitoring/prometheus/fritzbox-exporter.nix
-  ./services/monitoring/prometheus/json-exporter.nix
-  ./services/monitoring/prometheus/minio-exporter.nix
-  ./services/monitoring/prometheus/nginx-exporter.nix
-  ./services/monitoring/prometheus/node-exporter.nix
-  ./services/monitoring/prometheus/snmp-exporter.nix
-  ./services/monitoring/prometheus/unifi-exporter.nix
-  ./services/monitoring/prometheus/varnish-exporter.nix
+  ./services/monitoring/prometheus/exporters.nix
   ./services/monitoring/riemann.nix
   ./services/monitoring/riemann-dash.nix
   ./services/monitoring/riemann-tools.nix
@@ -396,14 +419,17 @@
   ./services/monitoring/vnstat.nix
   ./services/monitoring/zabbix-agent.nix
   ./services/monitoring/zabbix-server.nix
+  ./services/network-filesystems/beegfs.nix
   ./services/network-filesystems/cachefilesd.nix
+  ./services/network-filesystems/davfs2.nix
   ./services/network-filesystems/drbd.nix
   ./services/network-filesystems/glusterfs.nix
   ./services/network-filesystems/kbfs.nix
   ./services/network-filesystems/ipfs.nix
   ./services/network-filesystems/netatalk.nix
   ./services/network-filesystems/nfsd.nix
-  ./services/network-filesystems/openafs-client/default.nix
+  ./services/network-filesystems/openafs/client.nix
+  ./services/network-filesystems/openafs/server.nix
   ./services/network-filesystems/rsyncd.nix
   ./services/network-filesystems/samba.nix
   ./services/network-filesystems/tahoe.nix
@@ -411,7 +437,9 @@
   ./services/network-filesystems/u9fs.nix
   ./services/network-filesystems/yandex-disk.nix
   ./services/network-filesystems/xtreemfs.nix
+  ./services/network-filesystems/ceph.nix
   ./services/networking/amuled.nix
+  ./services/networking/aria2.nix
   ./services/networking/asterisk.nix
   ./services/networking/atftpd.nix
   ./services/networking/avahi-daemon.nix
@@ -476,6 +504,7 @@
   ./services/networking/minidlna.nix
   ./services/networking/miniupnpd.nix
   ./services/networking/mosquitto.nix
+  ./services/networking/monero.nix
   ./services/networking/miredo.nix
   ./services/networking/mstpd.nix
   ./services/networking/murmur.nix
@@ -486,6 +515,7 @@
   ./services/networking/ngircd.nix
   ./services/networking/nghttpx/default.nix
   ./services/networking/nix-serve.nix
+  ./services/networking/nixops-dns.nix
   ./services/networking/nntp-proxy.nix
   ./services/networking/nsd.nix
   ./services/networking/ntopng.nix
@@ -504,7 +534,7 @@
   ./services/networking/prayer.nix
   ./services/networking/privoxy.nix
   ./services/networking/prosody.nix
-  # ./services/networking/quagga.nix
+  ./services/networking/quagga.nix
   ./services/networking/quassel.nix
   ./services/networking/racoon.nix
   ./services/networking/radicale.nix
@@ -513,10 +543,12 @@
   ./services/networking/redsocks.nix
   ./services/networking/resilio.nix
   ./services/networking/rpcbind.nix
+  ./services/networking/rxe.nix
   ./services/networking/sabnzbd.nix
   ./services/networking/searx.nix
   ./services/networking/seeks.nix
   ./services/networking/skydns.nix
+  ./services/networking/shadowsocks.nix
   ./services/networking/shairport-sync.nix
   ./services/networking/shout.nix
   ./services/networking/sniproxy.nix
@@ -528,6 +560,7 @@
   ./services/networking/ssh/lshd.nix
   ./services/networking/ssh/sshd.nix
   ./services/networking/strongswan.nix
+  ./services/networking/stunnel.nix
   ./services/networking/supplicant.nix
   ./services/networking/supybot.nix
   ./services/networking/syncthing.nix
@@ -586,6 +619,7 @@
   ./services/system/cloud-init.nix
   ./services/system/dbus.nix
   ./services/system/earlyoom.nix
+  ./services/system/localtime.nix
   ./services/system/kerberos.nix
   ./services/system/nscd.nix
   ./services/system/saslauthd.nix
@@ -603,11 +637,10 @@
   ./services/web-apps/atlassian/jira.nix
   ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
-  ./services/web-apps/nixbot.nix
   ./services/web-apps/nexus.nix
   ./services/web-apps/pgpkeyserver-lite.nix
-  ./services/web-apps/piwik.nix
-  ./services/web-apps/pump.io.nix
+  ./services/web-apps/matomo.nix
+  ./services/web-apps/restya-board.nix
   ./services/web-apps/tt-rss.nix
   ./services/web-apps/selfoss.nix
   ./services/web-apps/quassel-webserver.nix
@@ -620,8 +653,10 @@
   ./services/web-servers/lighttpd/default.nix
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/lighttpd/inginious.nix
+  ./services/web-servers/mighttpd2.nix
   ./services/web-servers/minio.nix
   ./services/web-servers/nginx/default.nix
+  ./services/web-servers/nginx/gitweb.nix
   ./services/web-servers/phpfpm/default.nix
   ./services/web-servers/shellinabox.nix
   ./services/web-servers/tomcat.nix
@@ -666,8 +701,10 @@
   ./services/x11/xserver.nix
   ./system/activation/activation-script.nix
   ./system/activation/top-level.nix
+  ./system/boot/binfmt.nix
   ./system/boot/coredump.nix
   ./system/boot/emergency-mode.nix
+  ./system/boot/grow-partition.nix
   ./system/boot/initrd-network.nix
   ./system/boot/initrd-ssh.nix
   ./system/boot/kernel.nix
@@ -734,6 +771,7 @@
   ./virtualisation/lxcfs.nix
   ./virtualisation/lxd.nix
   ./virtualisation/amazon-options.nix
+  ./virtualisation/hyperv-guest.nix
   ./virtualisation/openvswitch.nix
   ./virtualisation/parallels-guest.nix
   ./virtualisation/rkt.nix
@@ -742,6 +780,4 @@
   ./virtualisation/vmware-guest.nix
   ./virtualisation/xen-dom0.nix
   ./virtualisation/xe-guest-utilities.nix
-  ./virtualisation/openstack/keystone.nix
-  ./virtualisation/openstack/glance.nix
 ]
diff --git a/nixos/modules/profiles/all-hardware.nix b/nixos/modules/profiles/all-hardware.nix
index 3c7e516c497f..f56640f19782 100644
--- a/nixos/modules/profiles/all-hardware.nix
+++ b/nixos/modules/profiles/all-hardware.nix
@@ -19,13 +19,12 @@
       "sata_sil" "sata_sil24" "sata_sis" "sata_svw" "sata_sx4"
       "sata_uli" "sata_via" "sata_vsc"
 
-      "pata_ali" "pata_amd" "pata_artop" "pata_atiixp"
-      "pata_cs5520" "pata_cs5530" "pata_cs5535" "pata_efar"
+      "pata_ali" "pata_amd" "pata_artop" "pata_atiixp" "pata_efar"
       "pata_hpt366" "pata_hpt37x" "pata_hpt3x2n" "pata_hpt3x3"
       "pata_it8213" "pata_it821x" "pata_jmicron" "pata_marvell"
       "pata_mpiix" "pata_netcell" "pata_ns87410" "pata_oldpiix"
       "pata_pcmcia" "pata_pdc2027x" "pata_qdi" "pata_rz1000"
-      "pata_sc1200" "pata_serverworks" "pata_sil680" "pata_sis"
+      "pata_serverworks" "pata_sil680" "pata_sis"
       "pata_sl82c105" "pata_triflex" "pata_via"
       "pata_winbond"
 
diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix
index 39b8553976eb..3bf06a951193 100644
--- a/nixos/modules/profiles/base.nix
+++ b/nixos/modules/profiles/base.nix
@@ -9,7 +9,7 @@
   environment.systemPackages = [
     pkgs.w3m-nox # needed for the manual anyway
     pkgs.testdisk # useful for repairing boot problems
-    pkgs.mssys # for writing Microsoft boot sectors / MBRs
+    pkgs.ms-sys # for writing Microsoft boot sectors / MBRs
     pkgs.efibootmgr
     pkgs.efivar
     pkgs.parted
diff --git a/nixos/modules/profiles/clone-config.nix b/nixos/modules/profiles/clone-config.nix
index 77d86f8d7405..5b4e68beb6a6 100644
--- a/nixos/modules/profiles/clone-config.nix
+++ b/nixos/modules/profiles/clone-config.nix
@@ -17,7 +17,7 @@ let
   # you should use files).
   moduleFiles =
     # FIXME: use typeOf (Nix 1.6.1).
-    filter (x: !isAttrs x && !builtins.isFunction x) modules;
+    filter (x: !isAttrs x && !lib.isFunction x) modules;
 
   # Partition module files because between NixOS and non-NixOS files.  NixOS
   # files may change if the repository is updated.
diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix
index ef6fd77b5f8d..c3ee6e98371e 100644
--- a/nixos/modules/profiles/demo.nix
+++ b/nixos/modules/profiles/demo.nix
@@ -10,4 +10,10 @@
       password = "demo";
       uid = 1000;
     };
+
+  services.xserver.displayManager.sddm.autoLogin = {
+    enable = true;
+    relogin = true;
+    user = "demo";
+  };
 }
diff --git a/nixos/modules/profiles/docker-container.nix b/nixos/modules/profiles/docker-container.nix
index 433492b96137..7031d7d1d593 100644
--- a/nixos/modules/profiles/docker-container.nix
+++ b/nixos/modules/profiles/docker-container.nix
@@ -14,9 +14,7 @@ in {
   ];
 
   # Create the tarball
-  system.build.tarball = import ../../lib/make-system-tarball.nix {
-    inherit (pkgs) stdenv perl xz pathsFromGraph;
-
+  system.build.tarball = pkgs.callPackage ../../lib/make-system-tarball.nix {
     contents = [];
     extraArgs = "--owner=0";
 
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index 506a6ee3eaa8..43f06c219f82 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -72,7 +72,13 @@ with lib;
 
     # To speed up installation a little bit, include the complete
     # stdenv in the Nix store on the CD.
-    system.extraDependencies = with pkgs; [ stdenv stdenvNoCC busybox ];
+    system.extraDependencies = with pkgs;
+      [
+        stdenv
+        stdenvNoCC # for runCommand
+        busybox
+        jq # for closureInfo
+      ];
 
     # Show all debug messages from the kernel but don't log refused packets
     # because we have the firewall enabled. This makes installs from the
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index e2497d04252e..40df7063a9bf 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -10,10 +10,9 @@ with lib;
 
   # This isn't perfect, but let's expect the user specifies an UTF-8 defaultLocale
   i18n.supportedLocales = [ (config.i18n.defaultLocale + "/UTF-8") ];
-  services.nixosManual.enable = mkDefault false;
 
-  programs.man.enable = mkDefault false;
-  programs.info.enable = mkDefault false;
+  documentation.enable = mkDefault false;
+  services.nixosManual.enable = mkDefault false;
 
   sound.enable = mkDefault false;
 }
diff --git a/nixos/modules/profiles/qemu-guest.nix b/nixos/modules/profiles/qemu-guest.nix
index 987eb051b98c..a1ec1d45395e 100644
--- a/nixos/modules/profiles/qemu-guest.nix
+++ b/nixos/modules/profiles/qemu-guest.nix
@@ -4,7 +4,7 @@
 { config, pkgs, ... }:
 
 {
-  boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_blk" "virtio_scsi" "9p" "9pnet_virtio" ];
+  boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_scsi" "9p" "9pnet_virtio" ];
   boot.initrd.kernelModules = [ "virtio_balloon" "virtio_console" "virtio_rng" ];
 
   boot.initrd.postDeviceCommands =
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix
index 18290555b79d..f648d70bd9fa 100644
--- a/nixos/modules/programs/adb.nix
+++ b/nixos/modules/programs/adb.nix
@@ -16,6 +16,7 @@ with lib;
           To grant access to a user, it must be part of adbusers group:
           <code>users.extraUsers.alice.extraGroups = ["adbusers"];</code>
         '';
+        relatedPackages = [ ["androidenv" "platformTools"] ];
       };
     };
   };
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index e23849d350b4..69a1a482d074 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -14,13 +14,16 @@ let
   bashCompletion = optionalString cfg.enableCompletion ''
     # Check whether we're running a version of Bash that has support for
     # programmable completion. If we do, enable all modules installed in
-    # the system (and user profile).
+    # the system and user profile in obsolete /etc/bash_completion.d/
+    # directories. Bash loads completions in all
+    # $XDG_DATA_DIRS/share/bash-completion/completions/
+    # on demand, so they do not need to be sourced here.
     if shopt -q progcomp &>/dev/null; then
       . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
       nullglobStatus=$(shopt -p nullglob)
       shopt -s nullglob
       for p in $NIX_PROFILES; do
-        for m in "$p/etc/bash_completion.d/"* "$p/share/bash-completion/completions/"*; do
+        for m in "$p/etc/bash_completion.d/"*; do
           . $m
         done
       done
@@ -107,7 +110,7 @@ in
       };
 
       enableCompletion = mkOption {
-        default = false;
+        default = true;
         description = ''
           Enable Bash completion for all interactive bash shells.
         '';
@@ -123,7 +126,7 @@ in
     programs.bash = {
 
       shellInit = ''
-        . ${config.system.build.setEnvironment}
+        ${config.system.build.setEnvironment.text}
 
         ${cfge.shellInit}
       '';
@@ -197,8 +200,9 @@ in
         fi
       '';
 
-    # Configuration for readline in bash.
-    environment.etc."inputrc".source = ./inputrc;
+    # Configuration for readline in bash. We use "option default"
+    # priority to allow user override using both .text and .source.
+    environment.etc."inputrc".source = mkOptionDefault ./inputrc;
 
     users.defaultUserShell = mkDefault pkgs.bashInteractive;
 
@@ -207,6 +211,9 @@ in
       "/share/bash-completion"
     ];
 
+    environment.systemPackages = optional cfg.enableCompletion
+      pkgs.nix-bash-completions;
+
     environment.shells =
       [ "/run/current-system/sw/bin/bash"
         "/var/run/current-system/sw/bin/bash"
diff --git a/nixos/modules/programs/browserpass.nix b/nixos/modules/programs/browserpass.nix
index a073c7e66eb8..5f8a44a9848e 100644
--- a/nixos/modules/programs/browserpass.nix
+++ b/nixos/modules/programs/browserpass.nix
@@ -18,5 +18,6 @@ with lib;
       "opt/chrome/native-messaging-hosts/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-host.json";
       "opt/chrome/policies/managed/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-policy.json";
     };
+    nixpkgs.config.firefox.enableBrowserpass = true;
   };
 }
diff --git a/nixos/modules/programs/ccache.nix b/nixos/modules/programs/ccache.nix
new file mode 100644
index 000000000000..874774c72b47
--- /dev/null
+++ b/nixos/modules/programs/ccache.nix
@@ -0,0 +1,83 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.ccache;
+in {
+  options.programs.ccache = {
+    # host configuration
+    enable = mkEnableOption "CCache";
+    cacheDir = mkOption {
+      type = types.path;
+      description = "CCache directory";
+      default = "/var/cache/ccache";
+    };
+    # target configuration
+    packageNames = mkOption {
+      type = types.listOf types.str;
+      description = "Nix top-level packages to be compiled using CCache";
+      default = [];
+      example = [ "wxGTK30" "qt48" "ffmpeg_3_3" "libav_all" ];
+    };
+  };
+
+  config = mkMerge [
+    # host configuration
+    (mkIf cfg.enable {
+      systemd.tmpfiles.rules = [ "d ${cfg.cacheDir} 0770 root nixbld -" ];
+
+      # "nix-ccache --show-stats" and "nix-ccache --clear"
+      security.wrappers.nix-ccache = {
+        group = "nixbld";
+        setgid = true;
+        source = pkgs.writeScript "nix-ccache.pl" ''
+          #!${pkgs.perl}/bin/perl
+
+          %ENV=( CCACHE_DIR => '${cfg.cacheDir}' );
+          sub untaint {
+            my $v = shift;
+            return '-C' if $v eq '-C' || $v eq '--clear';
+            return '-V' if $v eq '-V' || $v eq '--version';
+            return '-s' if $v eq '-s' || $v eq '--show-stats';
+            return '-z' if $v eq '-z' || $v eq '--zero-stats';
+            exec('${pkgs.ccache}/bin/ccache', '-h');
+          }
+          exec('${pkgs.ccache}/bin/ccache', map { untaint $_ } @ARGV);
+        '';
+      };
+    })
+
+    # target configuration
+    (mkIf (cfg.packageNames != []) {
+      nixpkgs.overlays = [
+        (self: super: genAttrs cfg.packageNames (pn: super.${pn}.override { stdenv = builtins.trace "with ccache: ${pn}" self.ccacheStdenv; }))
+
+        (self: super: {
+          ccacheWrapper = super.ccacheWrapper.override {
+            extraConfig = ''
+              export CCACHE_COMPRESS=1
+              export CCACHE_DIR="${cfg.cacheDir}"
+              export CCACHE_UMASK=007
+              if [ ! -d "$CCACHE_DIR" ]; then
+                echo "====="
+                echo "Directory '$CCACHE_DIR' does not exist"
+                echo "Please create it with:"
+                echo "  sudo mkdir -m0770 '$CCACHE_DIR'"
+                echo "  sudo chown root:nixbld '$CCACHE_DIR'"
+                echo "====="
+                exit 1
+              fi
+              if [ ! -w "$CCACHE_DIR" ]; then
+                echo "====="
+                echo "Directory '$CCACHE_DIR' is not accessible for user $(whoami)"
+                echo "Please verify its access permissions"
+                echo "====="
+                exit 1
+              fi
+            '';
+          };
+        })
+      ];
+    })
+  ];
+}
\ No newline at end of file
diff --git a/nixos/modules/programs/chromium.nix b/nixos/modules/programs/chromium.nix
index 54739feab976..41c49db8c71c 100644
--- a/nixos/modules/programs/chromium.nix
+++ b/nixos/modules/programs/chromium.nix
@@ -36,6 +36,7 @@ in
             "chlffgpmiacpedhhbkiomidkjlcfhogd" # pushbullet
             "mbniclmhobmnbdlbpiphghaielnnpgdp" # lightshot
             "gcbommkclmclpchllfjekcdonpmejbdp" # https everywhere
+            "cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin
           ]
         '';
       };
@@ -52,8 +53,7 @@ in
         description = "Chromium default search provider url.";
         default = null;
         example =
-          "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:
-        ↪searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
+          "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
       };
 
       defaultSearchProviderSuggestURL = mkOption {
@@ -79,7 +79,11 @@ in
   ###### implementation
 
   config = lib.mkIf cfg.enable {
+    # for chromium
     environment.etc."chromium/policies/managed/default.json".text = builtins.toJSON defaultProfile;
     environment.etc."chromium/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
+    # for google-chrome https://www.chromium.org/administrators/linux-quick-start
+    environment.etc."opt/chrome/policies/managed/default.json".text = builtins.toJSON defaultProfile;
+    environment.etc."opt/chrome/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
   };
 }
diff --git a/nixos/modules/programs/criu.nix b/nixos/modules/programs/criu.nix
new file mode 100644
index 000000000000..48cf5c88a9fc
--- /dev/null
+++ b/nixos/modules/programs/criu.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.criu;
+in {
+
+  options = {
+    programs.criu = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Install <command>criu</command> along with necessary kernel options.
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "CHECKPOINT_RESTORE")
+    ];
+    boot.kernel.features.criu = true;
+    environment.systemPackages = [ pkgs.criu ];
+  };
+
+}
diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix
index 1b7e20799819..b7d8a345e65c 100644
--- a/nixos/modules/programs/dconf.nix
+++ b/nixos/modules/programs/dconf.nix
@@ -1,7 +1,8 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
+
+with lib;
 
 let
-  inherit (lib) mkOption mkIf types mapAttrsToList;
   cfg = config.programs.dconf;
 
   mkDconfProfile = name: path:
@@ -13,6 +14,7 @@ in
 
   options = {
     programs.dconf = {
+      enable = mkEnableOption "dconf";
 
       profiles = mkOption {
         type = types.attrsOf types.path;
@@ -26,9 +28,15 @@ in
 
   ###### implementation
 
-  config = mkIf (cfg.profiles != {}) {
-    environment.etc =
+  config = mkIf (cfg.profiles != {} || cfg.enable) {
+    environment.etc = optionals (cfg.profiles != {})
       (mapAttrsToList mkDconfProfile cfg.profiles);
+
+    environment.variables.GIO_EXTRA_MODULES = optional cfg.enable
+      "${pkgs.gnome3.dconf.lib}/lib/gio/modules";
+    # https://github.com/NixOS/nixpkgs/pull/31891
+    #environment.variables.XDG_DATA_DIRS = optional cfg.enable
+    #  "$(echo ${pkgs.gnome3.gsettings-desktop-schemas}/share/gsettings-schemas/gsettings-desktop-schemas-*)";
   };
 
 }
diff --git a/nixos/modules/programs/digitalbitbox/default.nix b/nixos/modules/programs/digitalbitbox/default.nix
new file mode 100644
index 000000000000..7c727489c6c9
--- /dev/null
+++ b/nixos/modules/programs/digitalbitbox/default.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.digitalbitbox;
+in
+
+{
+  options.programs.digitalbitbox = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Installs the Digital Bitbox application and enables the complementary hardware module.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.digitalbitbox;
+      defaultText = "pkgs.digitalbitbox";
+      description = "The Digital Bitbox package to use. This can be used to install a package with udev rules that differ from the defaults.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    hardware.digitalbitbox = {
+      enable = true;
+      package = cfg.package;
+    };
+  };
+
+  meta = {
+    doc = ./doc.xml;
+    maintainers = with stdenv.lib.maintainers; [ vidbina ];
+  };
+}
diff --git a/nixos/modules/programs/digitalbitbox/doc.xml b/nixos/modules/programs/digitalbitbox/doc.xml
new file mode 100644
index 000000000000..7acbc2fc4dde
--- /dev/null
+++ b/nixos/modules/programs/digitalbitbox/doc.xml
@@ -0,0 +1,85 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-programs-digitalbitbox">
+
+  <title>Digital Bitbox</title>
+
+  <para>
+    Digital Bitbox is a hardware wallet and second-factor authenticator.
+  </para>
+
+  <para>
+    The <literal>digitalbitbox</literal> programs module may be
+    installed by setting <literal>programs.digitalbitbox</literal>
+    to <literal>true</literal> in a manner similar to
+
+    <programlisting>
+      programs.digitalbitbox.enable = true;
+    </programlisting>
+
+    and bundles the <literal>digitalbitbox</literal> package (see <xref
+      linkend="sec-digitalbitbox-package" />), which contains the
+    <literal>dbb-app</literal> and <literal>dbb-cli</literal> binaries,
+    along with the hardware module (see <xref
+      linkend="sec-digitalbitbox-hardware-module" />) which sets up the
+    necessary udev rules to access the device.
+  </para>
+
+  <para>
+    Enabling the digitalbitbox module is pretty much the easiest way to
+    get a Digital Bitbox device working on your system.
+  </para>
+
+  <para>
+    For more information, see
+    <link xlink:href="https://digitalbitbox.com/start_linux" />.
+  </para>
+
+  <section xml:id="sec-digitalbitbox-package">
+    <title>Package</title>
+
+    <para>
+      The binaries, <literal>dbb-app</literal> (a GUI tool) and
+      <literal>dbb-cli</literal> (a CLI tool), are available through the
+      <literal>digitalbitbox</literal> package which could be installed
+      as follows:
+
+      <programlisting>
+        environment.systemPackages = [
+          pkgs.digitalbitbox
+        ];
+      </programlisting>
+    </para>
+  </section>
+
+
+  <section xml:id="sec-digitalbitbox-hardware-module">
+    <title>Hardware</title>
+
+    <para>
+      The digitalbitbox hardware package enables the udev rules for
+      Digital Bitbox devices and may be installed as follows:
+
+      <programlisting>
+        hardware.digitalbitbox.enable = true;
+      </programlisting>
+    </para>
+
+    <para>
+      In order to alter the udev rules, one may provide different values for
+      the <literal>udevRule51</literal> and <literal>udevRule52</literal>
+      attributes by means of overriding as follows:
+
+      <programlisting>
+        programs.digitalbitbox = {
+          enable = true;
+          package = pkgs.digitalbitbox.override {
+            udevRule51 = "something else";
+          };
+        };
+      </programlisting>
+    </para>
+  </section>
+</chapter>
diff --git a/nixos/modules/programs/iftop.nix b/nixos/modules/programs/iftop.nix
new file mode 100644
index 000000000000..a98a9a8187d4
--- /dev/null
+++ b/nixos/modules/programs/iftop.nix
@@ -0,0 +1,18 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.iftop;
+in {
+  options = {
+    programs.iftop.enable = mkEnableOption "iftop + setcap wrapper";
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.iftop ];
+    security.wrappers.iftop = {
+      source = "${pkgs.iftop}/bin/iftop";
+      capabilities = "cap_net_raw+p";
+    };
+  };
+}
diff --git a/nixos/modules/programs/info.nix b/nixos/modules/programs/info.nix
deleted file mode 100644
index be6439dca5ad..000000000000
--- a/nixos/modules/programs/info.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-
-  options = {
-
-    programs.info.enable = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to enable info pages and the <command>info</command> command.
-      '';
-    };
-
-  };
-
-
-  config = mkIf config.programs.info.enable {
-
-    environment.systemPackages = [ pkgs.texinfoInteractive ];
-
-    environment.pathsToLink = [ "/info" "/share/info" ];
-
-    environment.extraOutputsToInstall = [ "info" ];
-
-  };
-
-}
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
new file mode 100644
index 000000000000..d39103a58057
--- /dev/null
+++ b/nixos/modules/programs/less.nix
@@ -0,0 +1,131 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.less;
+
+  configText = if (cfg.configFile != null) then (builtins.readFile cfg.configFile) else ''
+    #command
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.commands)
+    }
+    ${if cfg.clearDefaultCommands then "#stop" else ""}
+
+    #line-edit
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys)
+    }
+
+    #env
+    ${concatStringsSep "\n"
+      (mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables)
+    }
+  '';
+
+  lessKey = pkgs.runCommand "lesskey"
+            { src = pkgs.writeText "lessconfig" configText; }
+            "${pkgs.less}/bin/lesskey -o $out $src";
+
+in
+
+{
+  options = {
+
+    programs.less = {
+
+      enable = mkEnableOption "less";
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = literalExample "$${pkgs.my-configs}/lesskey";
+        description = ''
+          Path to lesskey configuration file.
+
+          <option>configFile</option> takes precedence over <option>commands</option>,
+          <option>clearDefaultCommands</option>, <option>lineEditingKeys</option>, and
+          <option>envVariables</option>.
+        '';
+      };
+
+      commands = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          "h" = "noaction 5\e(";
+          "l" = "noaction 5\e)";
+        };
+        description = "Defines new command keys.";
+      };
+
+      clearDefaultCommands = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Clear all default commands.
+          You should remember to set the quit key.
+          Otherwise you will not be able to leave less without killing it.
+        '';
+      };
+
+      lineEditingKeys = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          "\e" = "abort";
+        };
+        description = "Defines new line-editing keys.";
+      };
+
+      envVariables = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          LESS = "--quit-if-one-screen";
+        };
+        description = "Defines environment variables.";
+      };
+
+      lessopen = mkOption {
+        type = types.nullOr types.str;
+        default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s";
+        description = ''
+          Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed.
+        '';
+      };
+
+      lessclose = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          When less closes a file opened in such a way, it will call another program, called the input postprocessor, which may  perform  any  desired  clean-up  action (such  as deleting the replacement file created by LESSOPEN).
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.less ];
+
+    environment.variables = {
+      "LESSKEY_SYSTEM" = toString lessKey;
+    } // optionalAttrs (cfg.lessopen != null) {
+      "LESSOPEN" = cfg.lessopen;
+    } // optionalAttrs (cfg.lessclose != null) {
+      "LESSCLOSE" = cfg.lessclose;
+    };
+
+    warnings = optional (
+      cfg.clearDefaultCommands && (all (x: x != "quit") (attrValues cfg.commands))
+    ) ''
+      config.programs.less.clearDefaultCommands clears all default commands of less but there is no alternative binding for exiting.
+      Consider adding a binding for 'quit'.
+    '';
+  };
+
+  meta.maintainers = with maintainers; [ johnazoidberg ];
+
+}
diff --git a/nixos/modules/programs/man.nix b/nixos/modules/programs/man.nix
deleted file mode 100644
index 5b20a38d8856..000000000000
--- a/nixos/modules/programs/man.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-
-  options = {
-
-    programs.man.enable = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to enable manual pages and the <command>man</command> command.
-        This also includes "man" outputs of all <literal>systemPackages</literal>.
-      '';
-    };
-
-  };
-
-
-  config = mkIf config.programs.man.enable {
-
-    environment.systemPackages = [ pkgs.man-db ];
-
-    environment.pathsToLink = [ "/share/man" ];
-
-    environment.extraOutputsToInstall = [ "man" ];
-
-  };
-
-}
diff --git a/nixos/modules/programs/plotinus.nix b/nixos/modules/programs/plotinus.nix
new file mode 100644
index 000000000000..065e72d6c374
--- /dev/null
+++ b/nixos/modules/programs/plotinus.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.plotinus;
+in
+{
+  meta = {
+    maintainers = pkgs.plotinus.meta.maintainers;
+    doc = ./plotinus.xml;
+  };
+
+  ###### interface
+
+  options = {
+    programs.plotinus = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the Plotinus GTK+3 plugin.  Plotinus provides a
+          popup (triggered by Ctrl-Shift-P) to search the menus of a
+          compatible application.
+        '';
+        type = types.bool;
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.variables.XDG_DATA_DIRS = [ "${pkgs.plotinus}/share/gsettings-schemas/${pkgs.plotinus.name}" ];
+    environment.variables.GTK3_MODULES = [ "${pkgs.plotinus}/lib/libplotinus.so" ];
+  };
+}
diff --git a/nixos/modules/programs/plotinus.xml b/nixos/modules/programs/plotinus.xml
new file mode 100644
index 000000000000..85b0e023e6c1
--- /dev/null
+++ b/nixos/modules/programs/plotinus.xml
@@ -0,0 +1,25 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-program-plotinus">
+
+<title>Plotinus</title>
+
+<para><emphasis>Source:</emphasis> <filename>modules/programs/plotinus.nix</filename></para>
+
+<para><emphasis>Upstream documentation:</emphasis> <link xlink:href="https://github.com/p-e-w/plotinus"/></para>
+
+<para>Plotinus is a searchable command palette in every modern GTK+ application.</para>
+
+<para>When in a GTK+3 application and Plotinus is enabled, you can press <literal>Ctrl+Shift+P</literal> to open the command palette.  The command palette provides a searchable list of of all menu items in the application.</para>
+
+<para>To enable Plotinus, add the following to your <filename>configuration.nix</filename>:
+
+<programlisting>
+programs.plotinus.enable = true;
+</programlisting>
+
+</para>
+
+</chapter>
diff --git a/nixos/modules/programs/rootston.nix b/nixos/modules/programs/rootston.nix
new file mode 100644
index 000000000000..842d9e6cfb48
--- /dev/null
+++ b/nixos/modules/programs/rootston.nix
@@ -0,0 +1,103 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.rootston;
+
+  rootstonWrapped = pkgs.writeScriptBin "rootston" ''
+    #! ${pkgs.runtimeShell}
+    if [[ "$#" -ge 1 ]]; then
+      exec ${pkgs.rootston}/bin/rootston "$@"
+    else
+      ${cfg.extraSessionCommands}
+      exec ${pkgs.rootston}/bin/rootston -C ${cfg.configFile}
+    fi
+  '';
+in {
+  options.programs.rootston = {
+    enable = mkEnableOption ''
+      rootston, the reference compositor for wlroots. The purpose of rootston
+      is to test and demonstrate the features of wlroots (if you want a real
+      Wayland compositor you should e.g. use Sway instead). You can manually
+      start the compositor by running "rootston" from a terminal'';
+
+    extraSessionCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        # Define a keymap (US QWERTY is the default)
+        export XKB_DEFAULT_LAYOUT=de,us
+        export XKB_DEFAULT_VARIANT=nodeadkeys
+        export XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle,caps:escape
+      '';
+      description = ''
+        Shell commands executed just before rootston is started.
+      '';
+    };
+
+    extraPackages = mkOption {
+      type = with types; listOf package;
+      default = with pkgs; [
+        westonLite xwayland rofi
+      ];
+      defaultText = literalExample ''
+        with pkgs; [
+          westonLite xwayland rofi
+        ]
+      '';
+      example = literalExample "[ ]";
+      description = ''
+        Extra packages to be installed system wide.
+      '';
+    };
+
+    config = mkOption {
+      type = types.str;
+      default = ''
+        [keyboard]
+        meta-key = Logo
+
+        # Sway/i3 like Keybindings
+        # Maps key combinations with commands to execute
+        # Commands include:
+        # - "exit" to stop the compositor
+        # - "exec" to execute a shell command
+        # - "close" to close the current view
+        # - "next_window" to cycle through windows
+        [bindings]
+        Logo+Shift+e = exit
+        Logo+q = close
+        Logo+m = maximize
+        Alt+Tab = next_window
+        Logo+Return = exec weston-terminal
+        Logo+d = exec rofi -show run
+      '';
+      description = ''
+        Default configuration for rootston (used when called without any
+        parameters).
+      '';
+    };
+
+    configFile = mkOption {
+      type = types.path;
+      default = "/etc/rootston.ini";
+      example = literalExample "${pkgs.rootston}/etc/rootston.ini";
+      description = ''
+        Path to the default rootston configuration file (the "config" option
+        will have no effect if you change the path).
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."rootston.ini".text = cfg.config;
+    environment.systemPackages = [ rootstonWrapped ] ++ cfg.extraPackages;
+
+    hardware.opengl.enable = mkDefault true;
+    fonts.enableDefaultFonts = mkDefault true;
+    programs.dconf.enable = mkDefault true;
+  };
+
+  meta.maintainers = with lib.maintainers; [ primeos gnidorah ];
+}
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 0f3f42901bab..8ec4169207db 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -26,8 +26,9 @@ let
       # Ensure privacy for newly created home directories.
       UMASK        077
 
-      # Uncomment this to allow non-root users to change their account
-      #information.  This should be made configurable.
+      # Uncomment this and install chfn SUID to allow non-root
+      # users to change their account GECOS information.
+      # This should be made configurable.
       #CHFN_RESTRICT frwh
 
     '';
@@ -103,13 +104,12 @@ in
 
     security.wrappers = {
       su.source        = "${pkgs.shadow.su}/bin/su";
-      chfn.source      = "${pkgs.shadow.out}/bin/chfn";
+      sg.source        = "${pkgs.shadow.out}/bin/sg";
+      newgrp.source    = "${pkgs.shadow.out}/bin/newgrp";
       newuidmap.source = "${pkgs.shadow.out}/bin/newuidmap";
       newgidmap.source = "${pkgs.shadow.out}/bin/newgidmap";
     } // (if config.users.mutableUsers then {
       passwd.source    = "${pkgs.shadow.out}/bin/passwd";
-      sg.source        = "${pkgs.shadow.out}/bin/sg";
-      newgrp.source    = "${pkgs.shadow.out}/bin/newgrp";
     } else {});
   };
 }
diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix
new file mode 100644
index 000000000000..86153d933855
--- /dev/null
+++ b/nixos/modules/programs/singularity.nix
@@ -0,0 +1,20 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.singularity;
+in {
+  options.programs.singularity = {
+    enable = mkEnableOption "Singularity";
+  };
+
+  config = mkIf cfg.enable {
+      environment.systemPackages = [ pkgs.singularity ];
+      systemd.tmpfiles.rules = [ "d /var/singularity/mnt/session 0770 root root -"
+                                 "d /var/singularity/mnt/final 0770 root root -"
+                                 "d /var/singularity/mnt/overlay 0770 root root -"
+                                 "d /var/singularity/mnt/container 0770 root root -"
+                                 "d /var/singularity/mnt/source 0770 root root -"];
+  };
+
+}
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index 0935bf0cae71..36289080a82a 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -13,7 +13,7 @@ let
 
   askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper"
     ''
-      #! ${pkgs.stdenv.shell} -e
+      #! ${pkgs.runtimeShell} -e
       export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')"
       exec ${askPassword}
     '';
diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix
index 9070722c770b..d9503d6004ff 100644
--- a/nixos/modules/programs/sway.nix
+++ b/nixos/modules/programs/sway.nix
@@ -4,33 +4,42 @@ with lib;
 
 let
   cfg = config.programs.sway;
-  sway = pkgs.sway;
+  swayPackage = pkgs.sway;
 
-  swayWrapped = pkgs.writeScriptBin "sway" ''
-    #! ${pkgs.stdenv.shell}
-    ${cfg.extraSessionCommands}
-    PATH="${sway}/bin:$PATH"
-    exec ${pkgs.dbus.dbus-launch} --exit-with-session sway-setcap
+  swayWrapped = pkgs.writeShellScriptBin "sway" ''
+    if [[ "$#" -ge 1 ]]; then
+      exec sway-setcap "$@"
+    else
+      ${cfg.extraSessionCommands}
+      exec ${pkgs.dbus.dbus-launch} --exit-with-session sway-setcap
+    fi
   '';
   swayJoined = pkgs.symlinkJoin {
-    name = "sway-wrapped";
-    paths = [ swayWrapped sway ];
+    name = "sway-joined";
+    paths = [ swayWrapped swayPackage ];
   };
-in
-{
+in {
   options.programs.sway = {
-    enable = mkEnableOption "sway";
+    enable = mkEnableOption ''
+      the tiling Wayland compositor Sway. After adding yourself to the "sway"
+      group you can manually launch Sway by executing "sway" from a terminal.
+      If you call "sway" with any parameters the extraSessionCommands won't be
+      executed and Sway won't be launched with dbus-launch'';
 
     extraSessionCommands = mkOption {
-      default     = "";
-      type        = types.lines;
+      type = types.lines;
+      default = "";
       example = ''
-        export XKB_DEFAULT_LAYOUT=us,de
-        export XKB_DEFAULT_VARIANT=,nodeadkeys
-        export XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle,
+        # Define a keymap (US QWERTY is the default)
+        export XKB_DEFAULT_LAYOUT=de,us
+        export XKB_DEFAULT_VARIANT=nodeadkeys
+        export XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle,caps:escape
+        # Change the Keyboard repeat delay and rate
+        export WLC_REPEAT_DELAY=660
+        export WLC_REPEAT_RATE=25
       '';
       description = ''
-        Shell commands executed just before sway is started.
+        Shell commands executed just before Sway is started.
       '';
     };
 
@@ -39,9 +48,12 @@ in
       default = with pkgs; [
         i3status xwayland rxvt_unicode dmenu
       ];
+      defaultText = literalExample ''
+        with pkgs; [ i3status xwayland rxvt_unicode dmenu ];
+      '';
       example = literalExample ''
         with pkgs; [
-          i3status xwayland rxvt_unicode dmenu
+          i3lock light termite
         ]
       '';
       description = ''
@@ -54,7 +66,7 @@ in
     environment.systemPackages = [ swayJoined ] ++ cfg.extraPackages;
     security.wrappers.sway = {
       program = "sway-setcap";
-      source = "${sway}/bin/sway";
+      source = "${swayPackage}/bin/sway";
       capabilities = "cap_sys_ptrace,cap_sys_tty_config=eip";
       owner = "root";
       group = "sway";
@@ -62,8 +74,12 @@ in
     };
 
     users.extraGroups.sway = {};
+    security.pam.services.swaylock = {};
 
     hardware.opengl.enable = mkDefault true;
     fonts.enableDefaultFonts = mkDefault true;
+    programs.dconf.enable = mkDefault true;
   };
+
+  meta.maintainers = with lib.maintainers; [ gnidorah primeos ];
 }
diff --git a/nixos/modules/programs/systemtap.nix b/nixos/modules/programs/systemtap.nix
new file mode 100644
index 000000000000..fd84732cd412
--- /dev/null
+++ b/nixos/modules/programs/systemtap.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.systemtap;
+in {
+
+  options = {
+    programs.systemtap = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Install <command>systemtap</command> along with necessary kernel options.
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "DEBUG")
+    ];
+    boot.kernel.features.debug = true;
+    environment.systemPackages = [
+      config.boot.kernelPackages.systemtap
+    ];
+  };
+
+}
diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix
index ed1d88a420a2..4a60403a2827 100644
--- a/nixos/modules/programs/tmux.nix
+++ b/nixos/modules/programs/tmux.nix
@@ -61,7 +61,12 @@ in {
   options = {
     programs.tmux = {
 
-      enable = mkEnableOption "<command>tmux</command> - a <command>screen</command> replacement.";
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whenever to configure <command>tmux</command> system-wide.";
+        relatedPackages = [ "tmux" ];
+      };
 
       aggressiveResize = mkOption {
         default = false;
@@ -151,6 +156,15 @@ in {
         type = types.str;
         description = "Set the $TERM variable.";
       };
+
+      secureSocket = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Store tmux socket under /run, which is more secure than /tmp, but as a
+          downside it doesn't survive user logout.
+        '';
+      };
     };
   };
 
@@ -163,7 +177,7 @@ in {
       systemPackages = [ pkgs.tmux ];
 
       variables = {
-        TMUX_TMPDIR = ''''${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}'';
+        TMUX_TMPDIR = lib.optional cfg.secureSocket ''''${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}'';
       };
     };
   };
diff --git a/nixos/modules/programs/udevil.nix b/nixos/modules/programs/udevil.nix
new file mode 100644
index 000000000000..ba5670f9dfe9
--- /dev/null
+++ b/nixos/modules/programs/udevil.nix
@@ -0,0 +1,14 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.udevil;
+
+in {
+  options.programs.udevil.enable = mkEnableOption "udevil";
+
+  config = mkIf cfg.enable {
+    security.wrappers.udevil.source = "${lib.getBin pkgs.udevil}/bin/udevil";
+  };
+}
diff --git a/nixos/modules/programs/way-cooler.nix b/nixos/modules/programs/way-cooler.nix
new file mode 100644
index 000000000000..633e959be9f3
--- /dev/null
+++ b/nixos/modules/programs/way-cooler.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.way-cooler;
+  way-cooler = pkgs.way-cooler;
+
+  wcWrapped = pkgs.writeShellScriptBin "way-cooler" ''
+    ${cfg.extraSessionCommands}
+    exec ${pkgs.dbus.dbus-launch} --exit-with-session ${way-cooler}/bin/way-cooler
+  '';
+  wcJoined = pkgs.symlinkJoin {
+    name = "way-cooler-wrapped";
+    paths = [ wcWrapped way-cooler ];
+  };
+  configFile = readFile "${way-cooler}/etc/way-cooler/init.lua";
+  spawnBar = ''
+    util.program.spawn_at_startup("lemonbar");
+  '';
+in
+{
+  options.programs.way-cooler = {
+    enable = mkEnableOption "way-cooler";
+
+    extraSessionCommands = mkOption {
+      default     = "";
+      type        = types.lines;
+      example = ''
+        export XKB_DEFAULT_LAYOUT=us,de
+        export XKB_DEFAULT_VARIANT=,nodeadkeys
+        export XKB_DEFAULT_OPTIONS=grp:caps_toggle,
+      '';
+      description = ''
+        Shell commands executed just before way-cooler is started.
+      '';
+    };
+
+    extraPackages = mkOption {
+      type = with types; listOf package;
+      default = with pkgs; [
+        westonLite xwayland dmenu
+      ];
+      example = literalExample ''
+        with pkgs; [
+          westonLite xwayland dmenu
+        ]
+      '';
+      description = ''
+        Extra packages to be installed system wide.
+      '';
+    };
+
+    enableBar = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to enable an unofficial bar.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ wcJoined ] ++ cfg.extraPackages;
+
+    security.pam.services.wc-lock = {};
+    environment.etc."way-cooler/init.lua".text = ''
+      ${configFile}
+      ${optionalString cfg.enableBar spawnBar}
+    '';
+
+    hardware.opengl.enable = mkDefault true;
+    fonts.enableDefaultFonts = mkDefault true;
+    programs.dconf.enable = mkDefault true;
+  };
+
+  meta.maintainers = with maintainers; [ gnidorah ];
+}
diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix
new file mode 100644
index 000000000000..a01083c3ace9
--- /dev/null
+++ b/nixos/modules/programs/yabar.nix
@@ -0,0 +1,149 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.yabar;
+
+  mapExtra = v: lib.concatStringsSep "\n" (mapAttrsToList (
+    key: val: "${key} = ${if (isString val) then "\"${val}\"" else "${builtins.toString val}"};"
+  ) v);
+
+  listKeys = r: concatStringsSep "," (map (n: "\"${n}\"") (attrNames r));
+
+  configFile = let
+    bars = mapAttrsToList (
+      name: cfg: ''
+        ${name}: {
+          font: "${cfg.font}";
+          position: "${cfg.position}";
+
+          ${mapExtra cfg.extra}
+
+          block-list: [${listKeys cfg.indicators}]
+
+          ${concatStringsSep "\n" (mapAttrsToList (
+            name: cfg: ''
+              ${name}: {
+                exec: "${cfg.exec}";
+                align: "${cfg.align}";
+                ${mapExtra cfg.extra}
+              };
+            ''
+          ) cfg.indicators)}
+        };
+      ''
+    ) cfg.bars;
+  in pkgs.writeText "yabar.conf" ''
+    bar-list = [${listKeys cfg.bars}];
+    ${concatStringsSep "\n" bars}
+  '';
+in
+  {
+    options.programs.yabar = {
+      enable = mkEnableOption "yabar";
+
+      package = mkOption {
+        default = pkgs.yabar;
+        example = literalExample "pkgs.yabar-unstable";
+        type = types.package;
+
+        description = ''
+          The package which contains the `yabar` binary.
+
+          Nixpkgs provides the `yabar` and `yabar-unstable`
+          derivations since 18.03, so it's possible to choose.
+        '';
+      };
+
+      bars = mkOption {
+        default = {};
+        type = types.attrsOf(types.submodule {
+          options = {
+            font = mkOption {
+              default = "sans bold 9";
+              example = "Droid Sans, FontAwesome Bold 9";
+              type = types.string;
+
+              description = ''
+                The font that will be used to draw the status bar.
+              '';
+            };
+
+            position = mkOption {
+              default = "top";
+              example = "bottom";
+              type = types.enum [ "top" "bottom" ];
+
+              description = ''
+                The position where the bar will be rendered.
+              '';
+            };
+
+            extra = mkOption {
+              default = {};
+              type = types.attrsOf types.string;
+
+              description = ''
+                An attribute set which contains further attributes of a bar.
+              '';
+            };
+
+            indicators = mkOption {
+              default = {};
+              type = types.attrsOf(types.submodule {
+                options.exec = mkOption {
+                  example = "YABAR_DATE";
+                  type = types.string;
+                  description = ''
+                     The type of the indicator to be executed.
+                  '';
+                };
+
+                options.align = mkOption {
+                  default = "left";
+                  example = "right";
+                  type = types.enum [ "left" "center" "right" ];
+
+                  description = ''
+                    Whether to align the indicator at the left or right of the bar.
+                  '';
+                };
+
+                options.extra = mkOption {
+                  default = {};
+                  type = types.attrsOf (types.either types.string types.int);
+
+                  description = ''
+                    An attribute set which contains further attributes of a indicator.
+                  '';
+                };
+              });
+
+              description = ''
+                Indicators that should be rendered by yabar.
+              '';
+            };
+          };
+        });
+
+        description = ''
+          List of bars that should be rendered by yabar.
+        '';
+      };
+    };
+
+    config = mkIf cfg.enable {
+      systemd.user.services.yabar = {
+        description = "yabar service";
+        wantedBy = [ "graphical-session.target" ];
+        partOf = [ "graphical-session.target" ];
+
+        script = ''
+          ${cfg.package}/bin/yabar -c ${configFile}
+        '';
+
+        serviceConfig.Restart = "always";
+      };
+    };
+  }
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
index 9077643c4440..b995d390b279 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.nix
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -48,6 +48,15 @@ in
             Name of the theme to be used by oh-my-zsh.
           '';
         };
+
+        cacheDir = mkOption {
+          default = "$HOME/.cache/oh-my-zsh";
+          type = types.str;
+          description = ''
+            Cache directory to be used by `oh-my-zsh`.
+            Without this option it would default to the read-only nix store.
+          '';
+        };
       };
     };
 
@@ -74,6 +83,13 @@ in
           "ZSH_THEME=\"${cfg.theme}\""
         }
 
+        ${optionalString (cfg.cacheDir != null) ''
+          if [[ ! -d "${cfg.cacheDir}" ]]; then
+            mkdir -p "${cfg.cacheDir}"
+          fi
+          ZSH_CACHE_DIR=${cfg.cacheDir}
+        ''}
+
         source $ZSH/oh-my-zsh.sh
       '';
     };
diff --git a/nixos/modules/programs/zsh/zsh-autoenv.nix b/nixos/modules/programs/zsh/zsh-autoenv.nix
new file mode 100644
index 000000000000..630114bcda9f
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-autoenv.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.zsh-autoenv;
+in {
+  options = {
+    programs.zsh.zsh-autoenv = {
+      enable = mkEnableOption "zsh-autoenv";
+      package = mkOption {
+        default = pkgs.zsh-autoenv;
+        defaultText = "pkgs.zsh-autoenv";
+        description = ''
+          Package to install for `zsh-autoenv` usage.
+        '';
+
+        type = types.package;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.zsh.interactiveShellInit = ''
+      source ${cfg.package}/share/zsh-autoenv/autoenv.zsh
+    '';
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 6fb1346bbb33..f689250dc61f 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -36,8 +36,9 @@ in
       shellAliases = mkOption {
         default = config.environment.shellAliases;
         description = ''
-          Set of aliases for zsh shell. See <option>environment.shellAliases</option>
-          for an option format description.
+          Set of aliases for zsh shell. Overrides the default value taken from
+           <option>environment.shellAliases</option>.
+          See <option>environment.shellAliases</option> for an option format description.
         '';
         type = types.attrs; # types.attrsOf types.stringOrPath;
       };
@@ -107,7 +108,7 @@ in
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
         export __ETC_ZSHENV_SOURCED=1
 
-        . ${config.system.build.setEnvironment}
+        ${config.system.build.setEnvironment.text}
 
         ${cfge.shellInit}
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index f30cbe427f09..d8bea6267320 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -4,6 +4,7 @@ with lib;
 
 {
   imports = [
+    (mkRenamedOptionModule [ "dysnomia" ] [ "services" "dysnomia" ])
     (mkRenamedOptionModule [ "environment" "x11Packages" ] [ "environment" "systemPackages" ])
     (mkRenamedOptionModule [ "environment" "enableBashCompletion" ] [ "programs" "bash" "enableCompletion" ])
     (mkRenamedOptionModule [ "environment" "nix" ] [ "nix" "package" ])
@@ -22,6 +23,8 @@ with lib;
       (config:
         let enabled = getAttrFromPath [ "services" "printing" "gutenprint" ] config;
         in if enabled then [ pkgs.gutenprint ] else [ ]))
+    (mkRenamedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ])
+    (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
     (mkRenamedOptionModule [ "services" "elasticsearch" "host" ] [ "services" "elasticsearch" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "graphite" "api" "host" ] [ "services" "graphite" "api" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "graphite" "web" "host" ] [ "services" "graphite" "web" "listenAddress" ])
@@ -48,6 +51,10 @@ with lib;
     (mkRemovedOptionModule [ "services" "rmilter" "bindInetSockets" ] "Use services.rmilter.bindSocket.* instead")
     (mkRemovedOptionModule [ "services" "rmilter" "bindUnixSockets" ] "Use services.rmilter.bindSocket.* instead")
 
+    # Xsession script
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "xserver" "displayManager" "job" "logToFile" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "xserver" "displayManager" "job" "logToJournal" ])
+
     # Old Grub-related options.
     (mkRenamedOptionModule [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ])
     (mkRenamedOptionModule [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ])
@@ -78,6 +85,10 @@ with lib;
     (mkRenamedOptionModule [ "services" "virtualboxHost" "addNetworkInterface" ] [ "virtualisation" "virtualbox" "host" "addNetworkInterface" ])
     (mkRenamedOptionModule [ "services" "virtualboxHost" "enableHardening" ] [ "virtualisation" "virtualbox" "host" "enableHardening" ])
 
+    # libvirtd
+    (mkRemovedOptionModule [ "virtualisation" "libvirtd" "enableKVM" ]
+      "Set the option `virtualisation.libvirtd.qemuPackage' instead.")
+
     # Tarsnap
     (mkRenamedOptionModule [ "services" "tarsnap" "config" ] [ "services" "tarsnap" "archives" ])
 
@@ -140,6 +151,12 @@ with lib;
     # parsoid
     (mkRemovedOptionModule [ "services" "parsoid" "interwikis" ] [ "services" "parsoid" "wikis" ])
 
+    # piwik was renamed to matomo
+    (mkRenamedOptionModule [ "services" "piwik" "enable" ] [ "services" "matomo" "enable" ])
+    (mkRenamedOptionModule [ "services" "piwik" "webServerUser" ] [ "services" "matomo" "webServerUser" ])
+    (mkRenamedOptionModule [ "services" "piwik" "phpfpmProcessManagerConfig" ] [ "services" "matomo" "phpfpmProcessManagerConfig" ])
+    (mkRenamedOptionModule [ "services" "piwik" "nginx" ] [ "services" "matomo" "nginx" ])
+
     # tarsnap
     (mkRemovedOptionModule [ "services" "tarsnap" "cachedir" ] "Use services.tarsnap.archives.<name>.cachedir")
 
@@ -178,6 +195,21 @@ with lib;
     (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "config" "fonts" "fontconfig" "forceAutohint" ])
     (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "config" "fonts" "fontconfig" "renderMonoTTFAsBitmap" ])
 
+    # Profile splitting
+    (mkRenamedOptionModule [ "virtualization" "growPartition" ] [ "boot" "growPartition" ])
+
+    # misc/version.nix
+    #(mkRenamedOptionModule [ "config" "system" "nixosVersion" ] [ "config" "system" "nixos" "version" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosRelease" ] [ "config" "system" "nixos" "release" ])
+    #(mkRenamedOptionModule [ "config" "system" "nixosVersionSuffix" ] [ "config" "system" "nixos" "versionSuffix" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosRevision" ] [ "config" "system" "nixos" "revision" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosCodeName" ] [ "config" "system" "nixos" "codeName" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosLabel" ] [ "config" "system" "nixos" "label" ])
+
+    # Users
+    (mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ])
+    (mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ])
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
     (mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
@@ -194,11 +226,14 @@ with lib;
       "See the 16.09 release notes for more information.")
     (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
     (mkRemovedOptionModule [ "services" "dovecot2" "package" ] "")
+    (mkRemovedOptionModule [ "services" "firefox" "syncserver" "user" ] "")
+    (mkRemovedOptionModule [ "services" "firefox" "syncserver" "group" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "hinting" "style" ] "")
     (mkRemovedOptionModule [ "services" "xserver" "displayManager" "sddm" "themes" ]
       "Set the option `services.xserver.displayManager.sddm.package' instead.")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
+    (mkRemovedOptionModule [ "virtualisation" "xen" "qemu" ] "You don't need this option anymore, it will work without it.")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
@@ -209,5 +244,18 @@ with lib;
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ])
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ])
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ])
-  ];
+
+    # Xen
+    (mkRenamedOptionModule [ "virtualisation" "xen" "qemu-package" ] [ "virtualisation" "xen" "package-qemu" ])
+
+    (mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
+    (mkRenamedOptionModule [ "programs" "man"  "enable" ] [ "documentation" "man"  "enable" ])
+
+  ] ++ (flip map [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
+                   "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
+                   "snmpExporter" "unifiExporter" "varnishExporter" ]
+       (opt: mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] ''
+         The prometheus exporters are now configured using `services.prometheus.exporters'.
+         See the 18.03 release notes for more information.
+       '' ));
 }
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index df0b4986eb82..eb705007d028 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -6,10 +6,11 @@ let
 
   cfg = config.security.acme;
 
-  certOpts = { ... }: {
+  certOpts = { name, ... }: {
     options = {
       webroot = mkOption {
         type = types.str;
+        example = "/var/lib/acme/acme-challenges";
         description = ''
           Where the webroot of the HTTP vhost is located.
           <filename>.well-known/acme-challenge/</filename> directory
@@ -20,8 +21,8 @@ let
       };
 
       domain = mkOption {
-        type = types.nullOr types.str;
-        default = null;
+        type = types.str;
+        default = name;
         description = "Domain to fetch certificate for (defaults to the entry name)";
       };
 
@@ -48,7 +49,7 @@ let
         default = false;
         description = ''
           Give read permissions to the specified group
-          (<option>security.acme.group</option>) to read SSL private certificates.
+          (<option>security.acme.cert.&lt;name&gt;.group</option>) to read SSL private certificates.
         '';
       };
 
@@ -110,7 +111,7 @@ let
           }
         '';
         description = ''
-          Extra domain names for which certificates are to be issued, with their
+          A list of extra domain names, which are included in the one certificate to be issued, with their
           own server roots if needed.
         '';
       };
@@ -211,8 +212,9 @@ in
                 domain = if data.domain != null then data.domain else cert;
                 cpath = lpath + optionalString (data.activationDelay != null) ".staging";
                 lpath = "${cfg.directory}/${cert}";
+                cpath = "${cfg.directory}/${cert}";
                 rights = if data.allowKeysForGroup then "750" else "700";
-                cmdline = [ "-v" "-d" domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
+                cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
                           ++ optionals (data.email != null) [ "--email" data.email ]
                           ++ concatMap (p: [ "-f" p ]) data.plugins
                           ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
@@ -284,6 +286,7 @@ in
                 };
                 selfsignedService = {
                   description = "Create preliminary self-signed certificate for ${cert}";
+                  path = [ pkgs.openssl ];
                   preStart = ''
                       if [ ! -d '${cpath}' ]
                       then
@@ -294,37 +297,41 @@ in
                   '';
                   script = 
                     ''
-                      # Create self-signed key
-                      workdir="/run/acme-selfsigned-${cert}"
-                      ${pkgs.openssl.bin}/bin/openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048
-                      ${pkgs.openssl.bin}/bin/openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key
-                      ${pkgs.openssl.bin}/bin/openssl req -new -key $workdir/server.key -out $workdir/server.csr \
+                      workdir="$(mktemp -d)"
+
+                      # Create CA
+                      openssl genrsa -des3 -passout pass:x -out $workdir/ca.pass.key 2048
+                      openssl rsa -passin pass:x -in $workdir/ca.pass.key -out $workdir/ca.key
+                      openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \
+                        -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com"
+                      openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt
+
+                      # Create key
+                      openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048
+                      openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key
+                      openssl req -new -key $workdir/server.key -out $workdir/server.csr \
                         -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
-                      ${pkgs.openssl.bin}/bin/openssl x509 -req -days 1 -in $workdir/server.csr -signkey $workdir/server.key -out $workdir/server.crt
+                      openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \
+                        -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \
+                        -out $workdir/server.crt
 
-                      # Move key to destination
-                      mv $workdir/server.key ${cpath}/key.pem
-                      mv $workdir/server.crt ${cpath}/fullchain.pem
+                      # Copy key to destination
+                      cp $workdir/server.key ${cpath}/key.pem
 
-                      # Create full.pem for e.g. lighttpd (same format as "simp_le ... -f full.pem" creates)
-                      cat "${cpath}/key.pem" "${cpath}/fullchain.pem" > "${cpath}/full.pem"
+                      # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates)
+                      cat $workdir/{server.crt,ca.crt} > "${cpath}/fullchain.pem"
 
-                      # Clean up working directory
-                      rm $workdir/server.csr
-                      rm $workdir/server.pass.key
+                      # Create full.pem for e.g. lighttpd
+                      cat $workdir/{server.key,server.crt,ca.crt} > "${cpath}/full.pem"
 
                       # Give key acme permissions
-                      chmod ${rights} '${cpath}/key.pem'
-                      chown '${data.user}:${data.group}' '${cpath}/key.pem'
-                      chmod ${rights} '${cpath}/fullchain.pem'
-                      chown '${data.user}:${data.group}' '${cpath}/fullchain.pem'
-                      chmod ${rights} '${cpath}/full.pem'
-                      chown '${data.user}:${data.group}' '${cpath}/full.pem'
+                      chown '${data.user}:${data.group}' "${cpath}/"{key,fullchain,full}.pem
+                      chmod ${rights} "${cpath}/"{key,fullchain,full}.pem
                     '';
                   serviceConfig = {
                     Type = "oneshot";
-                    RuntimeDirectory = "acme-selfsigned-${cert}";
                     PermissionsStartOnly = true;
+                    PrivateTmp = true;
                     User = data.user;
                     Group = data.group;
                   };
diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix
index 7ac21fd96507..2b22bdd9f0ae 100644
--- a/nixos/modules/security/audit.nix
+++ b/nixos/modules/security/audit.nix
@@ -13,7 +13,7 @@ let
   };
 
   disableScript = pkgs.writeScript "audit-disable" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Explicitly disable everything, as otherwise journald might start it.
     auditctl -D
     auditctl -e 0 -a task,never
@@ -23,7 +23,7 @@ let
   # put in the store like this. At the same time, it doesn't feel like a huge deal and working
   # around that is a pain so I'm leaving it like this for now.
   startScript = pkgs.writeScript "audit-start" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Clear out any rules we may start with
     auditctl -D
 
@@ -43,7 +43,7 @@ let
   '';
 
   stopScript = pkgs.writeScript "audit-stop" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Clear the rules
     auditctl -D
 
diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix
index 9ca818e86ffa..df6108dede7c 100644
--- a/nixos/modules/security/duosec.nix
+++ b/nixos/modules/security/duosec.nix
@@ -25,14 +25,14 @@ let
   loginCfgFile = optional cfg.ssh.enable
     { source = pkgs.writeText "login_duo.conf" configFile;
       mode   = "0600";
-      uid    = config.ids.uids.sshd;
+      user   = "sshd";
       target = "duo/login_duo.conf";
     };
 
   pamCfgFile = optional cfg.pam.enable
     { source = pkgs.writeText "pam_duo.conf" configFile;
       mode   = "0600";
-      uid    = config.ids.uids.sshd;
+      user   = "sshd";
       target = "duo/pam_duo.conf";
     };
 in
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 2d6713311a45..48998285d89d 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -46,6 +46,18 @@ let
         '';
       };
 
+      googleAuthenticator = {
+        enable = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            If set, users with enabled Google Authenticator (created
+            <filename>~/.google_authenticator</filename>) will be required
+            to provide Google Authenticator token to log in.
+          '';
+        };
+      };
+
       usbAuth = mkOption {
         default = config.security.pam.usb.enable;
         type = types.bool;
@@ -222,6 +234,22 @@ let
           password, KDE will prompt separately after login.
         '';
       };
+      sssdStrictAccess = mkOption {
+        default = false;
+        type = types.bool;
+        description = "enforce sssd access control";
+      };
+
+      enableGnomeKeyring = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled, pam_gnome_keyring will attempt to automatically unlock the
+          user's default Gnome keyring upon login. If the user login password does
+          not match their keyring password, Gnome Keyring will prompt separately
+          after login.
+        '';
+      };
 
       text = mkOption {
         type = types.nullOr types.lines;
@@ -241,11 +269,13 @@ let
       text = mkDefault
         (''
           # Account management.
-          account sufficient pam_unix.so
+          account ${if cfg.sssdStrictAccess then "required" else "sufficient"} pam_unix.so
           ${optionalString use_ldap
               "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
-          ${optionalString config.services.sssd.enable
+          ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false)
               "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"}
+          ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess)
+              "account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so"}
           ${optionalString config.krb5.enable
               "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
 
@@ -273,7 +303,12 @@ let
           # prompts the user for password so we run it once with 'required' at an
           # earlier point and it will run again with 'sufficient' further down.
           # We use try_first_pass the second time to avoid prompting password twice
-          (optionalString (cfg.unixAuth && (config.security.pam.enableEcryptfs || cfg.pamMount || cfg.enableKwallet)) ''
+          (optionalString (cfg.unixAuth &&
+          (config.security.pam.enableEcryptfs
+            || cfg.pamMount
+            || cfg.enableKwallet
+            || cfg.enableGnomeKeyring
+            || cfg.googleAuthenticator.enable)) ''
               auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth
               ${optionalString config.security.pam.enableEcryptfs
                 "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
@@ -282,6 +317,10 @@ let
               ${optionalString cfg.enableKwallet
                 ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
                  " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
+              ${optionalString cfg.enableGnomeKeyring
+                ("auth optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so")}
+              ${optionalString cfg.googleAuthenticator.enable
+                  "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"}
             '') + ''
           ${optionalString cfg.unixAuth
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
@@ -351,6 +390,10 @@ let
           ${optionalString (cfg.enableKwallet)
               ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
                " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
+          ${optionalString (cfg.enableGnomeKeyring)
+              "session optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"}
+          ${optionalString (config.virtualisation.lxc.lxcfs.enable)
+               "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"}
         '');
     };
 
@@ -519,7 +562,6 @@ in
         ftp = {};
         i3lock = {};
         i3lock-color = {};
-        swaylock = {};
         screen = {};
         vlock = {};
         xlock = {};
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index cfd0595e63b7..24283e1d6165 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -8,6 +8,22 @@ let
 
   inherit (pkgs) sudo;
 
+  toUserString = user: if (isInt user) then "#${toString user}" else "${user}";
+  toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}";
+
+  toCommandOptionsString = options:
+    "${concatStringsSep ":" options}${optionalString (length options != 0) ":"} ";
+
+  toCommandsString = commands:
+    concatStringsSep ", " (
+      map (command:
+        if (isString command) then
+          command
+        else
+          "${toCommandOptionsString command.options}${command.command}"
+      ) commands
+    );
+
 in
 
 {
@@ -31,8 +47,8 @@ in
       default = true;
       description =
         ''
-          Whether users of the <code>wheel</code> group can execute
-          commands as super user without entering a password.
+          Whether users of the <code>wheel</code> group must
+          provide a password to run commands as super user via <command>sudo</command>.
         '';
       };
 
@@ -47,6 +63,97 @@ in
         '';
     };
 
+    security.sudo.extraRules = mkOption {
+      description = ''
+        Define specific rules to be in the <filename>sudoers</filename> file.
+      '';
+      default = [];
+      example = [
+        # Allow execution of any command by all users in group sudo,
+        # requiring a password.
+        { groups = [ "sudo" ]; commands = [ "ALL" ]; }
+
+        # Allow execution of "/home/root/secret.sh" by user `backup`, `database`
+        # and the group with GID `1006` without a password.
+        { users = [ "backup" ]; groups = [ 1006 ];
+          commands = [ { command = "/home/root/secret.sh"; options = [ "SETENV" "NOPASSWD" ]; } ]; }
+
+        # Allow all users of group `bar` to run two executables as user `foo`
+        # with arguments being pre-set.
+        { groups = [ "bar" ]; runAs = "foo";
+          commands =
+            [ "/home/baz/cmd1.sh hello-sudo"
+              { command = ''/home/baz/cmd2.sh ""''; options = [ "SETENV" ]; } ]; }
+      ];
+      type = with types; listOf (submodule {
+        options = {
+          users = mkOption {
+            type = with types; listOf (either string int);
+            description = ''
+              The usernames / UIDs this rule should apply for.
+            '';
+            default = [];
+          };
+
+          groups = mkOption {
+            type = with types; listOf (either string int);
+            description = ''
+              The groups / GIDs this rule should apply for.
+            '';
+            default = [];
+          };
+
+          host = mkOption {
+            type = types.string;
+            default = "ALL";
+            description = ''
+              For what host this rule should apply.
+            '';
+          };
+
+          runAs = mkOption {
+            type = with types; string;
+            default = "ALL:ALL";
+            description = ''
+              Under which user/group the specified command is allowed to run.
+
+              A user can be specified using just the username: <code>"foo"</code>.
+              It is also possible to specify a user/group combination using <code>"foo:bar"</code>
+              or to only allow running as a specific group with <code>":bar"</code>.
+            '';
+          };
+
+          commands = mkOption {
+            description = ''
+              The commands for which the rule should apply.
+            '';
+            type = with types; listOf (either string (submodule {
+
+              options = {
+                command = mkOption {
+                  type = with types; string;
+                  description = ''
+                    A command being either just a path to a binary to allow any arguments,
+                    the full command with arguments pre-set or with <code>""</code> used as the argument,
+                    not allowing arguments to the command at all.
+                  '';
+                };
+
+                options = mkOption {
+                  type = with types; listOf (enum [ "NOPASSWD" "PASSWD" "NOEXEC" "EXEC" "SETENV" "NOSETENV" "LOG_INPUT" "NOLOG_INPUT" "LOG_OUTPUT" "NOLOG_OUTPUT" ]);
+                  description = ''
+                    Options for running the command. Refer to the <a href="https://www.sudo.ws/man/1.7.10/sudoers.man.html">sudo manual</a>.
+                  '';
+                  default = [];
+                };
+              };
+
+            }));
+          };
+        };
+      });
+    };
+
     security.sudo.extraConfig = mkOption {
       type = types.lines;
       default = "";
@@ -61,10 +168,16 @@ in
 
   config = mkIf cfg.enable {
 
+    security.sudo.extraRules = [
+      { groups = [ "wheel" ];
+        commands = [ { command = "ALL"; options = (if cfg.wheelNeedsPassword then [ "SETENV" ] else [ "NOPASSWD" "SETENV" ]); } ];
+      }
+    ];
+
     security.sudo.configFile =
       ''
         # Don't edit this file. Set the NixOS options ‘security.sudo.configFile’
-        # or ‘security.sudo.extraConfig’ instead.
+        # or ‘security.sudo.extraRules’ instead.
 
         # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic.
         Defaults env_keep+=SSH_AUTH_SOCK
@@ -72,8 +185,18 @@ in
         # "root" is allowed to do anything.
         root        ALL=(ALL:ALL) SETENV: ALL
 
-        # Users in the "wheel" group can do anything.
-        %wheel      ALL=(ALL:ALL) ${if cfg.wheelNeedsPassword then "" else "NOPASSWD: ALL, "}SETENV: ALL
+        # extraRules
+        ${concatStringsSep "\n" (
+          lists.flatten (
+            map (
+              rule: if (length rule.commands != 0) then [
+                (map (user: "${toUserString user}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.users)
+                (map (group: "${toGroupString group}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.groups)
+              ] else []
+            ) cfg.extraRules
+          )
+        )}
+
         ${cfg.extraConfig}
       '';
 
@@ -92,7 +215,7 @@ in
           { src = pkgs.writeText "sudoers-in" cfg.configFile; }
           # Make sure that the sudoers file is syntactically valid.
           # (currently disabled - NIXOS-66)
-          "${pkgs.sudo}/sbin/visudo -f $src -c && cp $src $out";
+          "${pkgs.buildPackages.sudo}/sbin/visudo -f $src -c && cp $src $out";
         target = "sudoers";
         mode = "0440";
       };
diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix
index 1f64213accd4..77e4b2a616d8 100644
--- a/nixos/modules/security/wrappers/default.nix
+++ b/nixos/modules/security/wrappers/default.nix
@@ -17,7 +17,7 @@ let
     hardeningEnable = [ "pie" ];
     installPhase = ''
       mkdir -p $out/bin
-      gcc -Wall -O2 -DWRAPPER_DIR=\"${parentWrapperDir}\" \
+      $CC -Wall -O2 -DWRAPPER_DIR=\"${parentWrapperDir}\" \
           -lcap-ng -lcap ${./wrapper.c} -o $out/bin/security-wrapper
     '';
   };
@@ -79,7 +79,7 @@ let
                  ({ owner = "root";
                     group = "root";
                   } // s)
-          else if 
+          else if
              (s ? "setuid" && s.setuid) ||
              (s ? "setgid" && s.setgid) ||
              (s ? "permissions")
diff --git a/nixos/modules/security/wrappers/wrapper.c b/nixos/modules/security/wrappers/wrapper.c
index 7091e314bb22..494e9e93ac22 100644
--- a/nixos/modules/security/wrappers/wrapper.c
+++ b/nixos/modules/security/wrappers/wrapper.c
@@ -10,8 +10,8 @@
 #include <errno.h>
 #include <linux/capability.h>
 #include <sys/capability.h>
-#include <linux/prctl.h>
 #include <sys/prctl.h>
+#include <limits.h>
 #include <cap-ng.h>
 
 // Make sure assertions are not compiled out, we use them to codify
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
index acf48d3c3d03..e3e8bb28c58b 100644
--- a/nixos/modules/services/audio/alsa.nix
+++ b/nixos/modules/services/audio/alsa.nix
@@ -21,7 +21,7 @@ in
 
       enable = mkOption {
         type = types.bool;
-        default = true;
+        default = false;
         description = ''
           Whether to enable ALSA sound.
         '';
diff --git a/nixos/modules/services/audio/mopidy.nix b/nixos/modules/services/audio/mopidy.nix
index c0a0f0374294..52613d450b51 100644
--- a/nixos/modules/services/audio/mopidy.nix
+++ b/nixos/modules/services/audio/mopidy.nix
@@ -4,17 +4,22 @@ with pkgs;
 with lib;
 
 let
-
   uid = config.ids.uids.mopidy;
   gid = config.ids.gids.mopidy;
   cfg = config.services.mopidy;
 
   mopidyConf = writeText "mopidy.conf" cfg.configuration;
 
-  mopidyEnv = python.buildEnv.override {
-    extraLibs = [ mopidy ] ++ cfg.extensionPackages;
+  mopidyEnv = buildEnv {
+    name = "mopidy-with-extensions-${mopidy.version}";
+    paths = closePropagation cfg.extensionPackages;
+    pathsToLink = [ "/${python.sitePackages}" ];
+    buildInputs = [ makeWrapper ];
+    postBuild = ''
+      makeWrapper ${mopidy}/bin/mopidy $out/bin/mopidy \
+        --prefix PYTHONPATH : $out/${python.sitePackages}
+    '';
   };
-
 in {
 
   options = {
@@ -61,7 +66,6 @@ in {
 
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/services/backup/almir.nix b/nixos/modules/services/backup/almir.nix
deleted file mode 100644
index fbb4ff4034f1..000000000000
--- a/nixos/modules/services/backup/almir.nix
+++ /dev/null
@@ -1,173 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.almir;
-
-  bconsoleconf = pkgs.writeText "bconsole.conf"
-    ''
-      Director {
-        Name = ${cfg.director_name}
-        DIRport = ${toString cfg.director_port}
-        address = ${cfg.director_address}
-        Password = "${cfg.director_password}"
-      }
-    '';
-
-  productionini = pkgs.writeText "production.ini"
-    ''
-[app:main]
-use = egg:almir
-
-pyramid.reload_templates = false
-pyramid.debug_authorization = false
-pyramid.debug_notfound = false
-pyramid.debug_routematch = false
-pyramid.debug_templates = false
-pyramid.default_locale_name = en
-pyramid.includes =
-    pyramid_exclog
-exclog.extra_info = true
-
-sqlalchemy.url = ${cfg.sqlalchemy_engine_url}
-timezone = ${cfg.timezone}
-bconsole_config = ${bconsoleconf}
-
-[server:main]
-use = egg:waitress#main
-host = 127.0.0.1
-port = ${toString cfg.port}
-
-
-# Begin logging configuration
-
-[loggers]
-keys = root, almir, sqlalchemy, exc_logger
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = WARN
-handlers = console
-
-[logger_almir]
-level = WARN
-handlers =
-qualname = almir
-
-[logger_exc_logger]
-level = ERROR
-handlers =
-qualname = exc_logger
-
-[logger_sqlalchemy]
-level = WARN
-handlers =
-qualname = sqlalchemy.engine
-# "level = INFO" logs SQL queries.
-# "level = DEBUG" logs SQL queries and results.
-# "level = WARN" logs neither.  (Recommended for production systems.)
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-    '';
-in {
-  options = {
-    services.almir = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable Almir web server. Also configures postgresql database and installs bacula.
-        '';
-      };
-
-      port = mkOption {
-        default = 35000;
-        type = types.int;
-        description = ''
-          Port for Almir web server to listen on.
-        '';
-      };
-
-      timezone = mkOption {
-	description = ''
-         Timezone as specified in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-        '';
-        example = "Europe/Ljubljana";
-      };
-
-      sqlalchemy_engine_url = mkOption {
-        default = "postgresql:///bacula";
-        example = ''
-          postgresql://bacula:bacula@localhost:5432/bacula
-          mysql+mysqlconnector://<user>:<password>@<hostname>/<database>'
-          sqlite:////var/lib/bacula/bacula.db'
-        '';
-	description = ''
-         Define SQL database connection to bacula catalog as specified in http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
-        '';
-      };
-
-      director_name = mkOption {
-        description = ''
-          Name of the Director to connect with bconsole.
-        '';
-      };
-
-      director_password = mkOption {
-        description = ''
-          Password for Director to connect with bconsole.
-        '';
-      };
-
-      director_port = mkOption {
-        default = 9101;
-        type = types.int;
-        description = ''
-          Port for Director to connect with bconsole.
-        '';
-      };
-
-      director_address = mkOption {
-        default = "127.0.0.1";
-        description = ''
-          IP/Hostname for Director to connect with bconsole.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services.almir = {
-      after = [ "network.target" "postgresql.service" ];
-      description = "Almir web app";
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.pythonPackages.almir ];
-      environment.PYTHONPATH = "${pkgs.pythonPackages.almir}/lib/${pkgs.pythonPackages.python.libPrefix}/site-packages";
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.pyramid}/bin/pserve ${productionini}";
-    };
-
-    environment.systemPackages = [ pkgs.pythonPackages.almir ];
-
-    users.extraUsers.almir = {
-      group = "almir";
-      uid = config.ids.uids.almir;
-      createHome = true;
-      shell = "${pkgs.bash}/bin/bash";
-    };
-
-    users.extraGroups.almir.gid = config.ids.gids.almir;
-  };
-}
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
new file mode 100644
index 000000000000..1b730e0c2b76
--- /dev/null
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -0,0 +1,580 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  isLocalPath = x:
+    builtins.substring 0 1 x == "/"      # absolute path
+    || builtins.substring 0 1 x == "."   # relative path
+    || builtins.match "[.*:.*]" == null; # not machine:path
+ 
+  mkExcludeFile = cfg:
+    # Write each exclude pattern to a new line
+    pkgs.writeText "excludefile" (concatStringsSep "\n" cfg.exclude);
+
+  mkKeepArgs = cfg:
+    # If cfg.prune.keep e.g. has a yearly attribute,
+    # its content is passed on as --keep-yearly
+    concatStringsSep " "
+      (mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep);
+
+  mkBackupScript = cfg: ''
+    on_exit()
+    {
+      exitStatus=$?
+      # Reset the EXIT handler, or else we're called again on 'exit' below
+      trap - EXIT
+      ${cfg.postHook}
+      exit $exitStatus
+    }
+    trap 'on_exit' INT TERM QUIT EXIT
+
+    archiveName="${cfg.archiveBaseName}-$(date ${cfg.dateFormat})"
+    archiveSuffix="${optionalString cfg.appendFailedSuffix ".failed"}"
+    ${cfg.preHook}
+  '' + optionalString cfg.doInit ''
+    # Run borg init if the repo doesn't exist yet
+    if ! borg list > /dev/null; then
+      borg init \
+        --encryption ${cfg.encryption.mode} \
+        $extraInitArgs
+      ${cfg.postInit}
+    fi
+  '' + ''
+    borg create \
+      --compression ${cfg.compression} \
+      --exclude-from ${mkExcludeFile cfg} \
+      $extraCreateArgs \
+      "::$archiveName$archiveSuffix" \
+      ${escapeShellArgs cfg.paths}
+  '' + optionalString cfg.appendFailedSuffix ''
+    borg rename "::$archiveName$archiveSuffix" "$archiveName"
+  '' + ''
+    ${cfg.postCreate}
+  '' + optionalString (cfg.prune.keep != { }) ''
+    borg prune \
+      ${mkKeepArgs cfg} \
+      --prefix ${escapeShellArg cfg.prune.prefix} \
+      $extraPruneArgs
+    ${cfg.postPrune}
+  '';
+
+  mkPassEnv = cfg: with cfg.encryption;
+    if passCommand != null then
+      { BORG_PASSCOMMAND = passCommand; }
+    else if passphrase != null then
+      { BORG_PASSPHRASE = passphrase; }
+    else { };
+
+  mkBackupService = name: cfg: 
+    let
+      userHome = config.users.users.${cfg.user}.home;
+    in nameValuePair "borgbackup-job-${name}" {
+      description = "BorgBackup job ${name}";
+      path = with pkgs; [
+        borgbackup openssh
+      ];
+      script = mkBackupScript cfg;
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        # Only run when no other process is using CPU or disk
+        CPUSchedulingPolicy = "idle";
+        IOSchedulingClass = "idle";
+        ProtectSystem = "strict";
+        ReadWritePaths =
+          [ "${userHome}/.config/borg" "${userHome}/.cache/borg" ]
+          # Borg needs write access to repo if it is not remote
+          ++ optional (isLocalPath cfg.repo) cfg.repo;
+        PrivateTmp = true;
+      };
+      environment = {
+        BORG_REPO = cfg.repo;
+        inherit (cfg) extraInitArgs extraCreateArgs extraPruneArgs;
+      } // (mkPassEnv cfg) // cfg.environment;
+      inherit (cfg) startAt;
+    };
+
+  # Paths listed in ReadWritePaths must exist before service is started
+  mkActivationScript = name: cfg:
+    let
+      install = "install -o ${cfg.user} -g ${cfg.group}";
+    in
+      nameValuePair "borgbackup-job-${name}" (stringAfter [ "users" ] (''
+        # Eensure that the home directory already exists
+        # We can't assert createHome == true because that's not the case for root
+        cd "${config.users.users.${cfg.user}.home}"                                                                                                         
+        ${install} -d .config/borg
+        ${install} -d .cache/borg
+      '' + optionalString (isLocalPath cfg.repo) ''
+        ${install} -d ${escapeShellArg cfg.repo}
+      ''));
+
+  mkPassAssertion = name: cfg: {
+    assertion = with cfg.encryption;
+      mode != "none" -> passCommand != null || passphrase != null;
+    message =
+      "passCommand or passphrase has to be specified because"
+      + '' borgbackup.jobs.${name}.encryption != "none"'';
+  };
+
+  mkRepoService = name: cfg:
+    nameValuePair "borgbackup-repo-${name}" {
+      description = "Create BorgBackup repository ${name} directory";
+      script = ''
+        mkdir -p ${escapeShellArg cfg.path}
+        chown ${cfg.user}:${cfg.group} ${escapeShellArg cfg.path}
+      '';
+      serviceConfig = {
+        # The service's only task is to ensure that the specified path exists
+        Type = "oneshot";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+  mkAuthorizedKey = cfg: appendOnly: key:
+    let
+      # Because of the following line, clients do not need to specify an absolute repo path
+      cdCommand = "cd ${escapeShellArg cfg.path}";
+      restrictedArg = "--restrict-to-${if cfg.allowSubRepos then "path" else "repository"} .";
+      appendOnlyArg = optionalString appendOnly "--append-only";
+      quotaArg = optionalString (cfg.quota != null) "--storage-quota ${cfg.quota}";
+      serveCommand = "borg serve ${restrictedArg} ${appendOnlyArg} ${quotaArg}";
+    in
+      ''command="${cdCommand} && ${serveCommand}",restrict ${key}'';
+
+  mkUsersConfig = name: cfg: {
+    users.${cfg.user} = {
+      openssh.authorizedKeys.keys =
+        (map (mkAuthorizedKey cfg false) cfg.authorizedKeys
+        ++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly);
+      useDefaultShell = true;
+    };
+    groups.${cfg.group} = { };
+  };
+
+  mkKeysAssertion = name: cfg: {
+    assertion = cfg.authorizedKeys != [ ] || cfg.authorizedKeysAppendOnly != [ ];
+    message =
+      "borgbackup.repos.${name} does not make sense"
+      + " without at least one public key";
+  };
+
+in {
+  meta.maintainers = with maintainers; [ dotlambda ];
+
+  ###### interface
+
+  options.services.borgbackup.jobs = mkOption {
+    description = "Deduplicating backups using BorgBackup.";
+    default = { };
+    example = literalExample ''
+      {
+        rootBackup = {
+          paths = "/";
+          exclude = [ "/nix" ];
+          repo = "/path/to/local/repo";
+          encryption = {
+            mode = "repokey";
+            passphrase = "secret";
+          };
+          compression = "auto,lzma";
+          startAt = "weekly";
+        };
+      }
+    '';
+    type = types.attrsOf (types.submodule (let globalConfig = config; in
+      { name, config, ... }: {
+        options = {
+
+          paths = mkOption {
+            type = with types; either path (nonEmptyListOf path);
+            description = "Path(s) to back up.";
+            example = "/home/user";
+            apply = x: if isList x then x else [ x ];
+          };
+
+          repo = mkOption {
+            type = types.str;
+            description = "Remote or local repository to back up to.";
+            example = "user@machine:/path/to/repo";
+          };
+
+          archiveBaseName = mkOption {
+            type = types.strMatching "[^/{}]+";
+            default = "${globalConfig.networking.hostName}-${name}";
+            defaultText = "\${config.networking.hostName}-<name>";
+            description = ''
+              How to name the created archives. A timestamp, whose format is
+              determined by <option>dateFormat</option>, will be appended. The full
+              name can be modified at runtime (<literal>$archiveName</literal>).
+              Placeholders like <literal>{hostname}</literal> must not be used.
+            '';
+          };
+
+          dateFormat = mkOption {
+            type = types.str;
+            description = ''
+              Arguments passed to <command>date</command>
+              to create a timestamp suffix for the archive name.
+            '';
+            default = "+%Y-%m-%dT%H:%M:%S";
+            example = "-u +%s";
+          };
+
+          startAt = mkOption {
+            type = with types; either str (listOf str);
+            default = "daily";
+            description = ''
+              When or how often the backup should run.
+              Must be in the format described in
+              <citerefentry><refentrytitle>systemd.time</refentrytitle>
+              <manvolnum>7</manvolnum></citerefentry>.
+              If you do not want the backup to start
+              automatically, use <literal>[ ]</literal>.
+            '';
+          };
+
+          user = mkOption {
+            type = types.str;
+            description = ''
+              The user <command>borg</command> is run as.
+              User or group need read permission
+              for the specified <option>paths</option>.
+            '';
+            default = "root";
+          };
+
+          group = mkOption {
+            type = types.str;
+            description = ''
+              The group borg is run as. User or group needs read permission
+              for the specified <option>paths</option>.
+            '';
+            default = "root";
+          };
+
+          encryption.mode = mkOption {
+            type = types.enum [
+              "repokey" "keyfile"
+              "repokey-blake2" "keyfile-blake2"
+              "authenticated" "authenticated-blake2"
+              "none"
+            ];
+            description = ''
+              Encryption mode to use. Setting a mode
+              other than <literal>"none"</literal> requires
+              you to specify a <option>passCommand</option>
+              or a <option>passphrase</option>.
+            '';
+          };
+
+          encryption.passCommand = mkOption {
+            type = with types; nullOr str;
+            description = ''
+              A command which prints the passphrase to stdout.
+              Mutually exclusive with <option>passphrase</option>.
+            '';
+            default = null;
+            example = "cat /path/to/passphrase_file";
+          };
+
+          encryption.passphrase = mkOption {
+            type = with types; nullOr str;
+            description = ''
+              The passphrase the backups are encrypted with.
+              Mutually exclusive with <option>passCommand</option>.
+              If you do not want the passphrase to be stored in the
+              world-readable Nix store, use <option>passCommand</option>.
+            '';
+            default = null;
+          };
+
+          compression = mkOption {
+            # "auto" is optional,
+            # compression mode must be given,
+            # compression level is optional
+            type = types.strMatching "none|(auto,)?(lz4|zstd|zlib|lzma)(,[[:digit:]]{1,2})?";
+            description = ''
+              Compression method to use. Refer to
+              <command>borg help compression</command>
+              for all available options.
+            '';
+            default = "lz4";
+            example = "auto,lzma";
+          };
+
+          exclude = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Exclude paths matching any of the given patterns. See
+              <command>borg help patterns</command> for pattern syntax.
+            '';
+            default = [ ];
+            example = [
+              "/home/*/.cache"
+              "/nix"
+            ];
+          };
+
+          doInit = mkOption {
+            type = types.bool;
+            description = ''
+              Run <command>borg init</command> if the
+              specified <option>repo</option> does not exist.
+              You should set this to <literal>false</literal>
+              if the repository is located on an external drive
+              that might not always be mounted.
+            '';
+            default = true;
+          };
+
+          appendFailedSuffix = mkOption {
+            type = types.bool;
+            description = ''
+              Append a <literal>.failed</literal> suffix
+              to the archive name, which is only removed if
+              <command>borg create</command> has a zero exit status.
+            '';
+            default = true;
+          };
+
+          prune.keep = mkOption {
+            # Specifying e.g. `prune.keep.yearly = -1`
+            # means there is no limit of yearly archives to keep
+            # The regex is for use with e.g. --keep-within 1y
+            type = with types; attrsOf (either int (strMatching "[[:digit:]]+[Hdwmy]"));
+            description = ''
+              Prune a repository by deleting all archives not matching any of the
+              specified retention options. See <command>borg help prune</command>
+              for the available options.
+            '';
+            default = { };
+            example = literalExample ''
+              {
+                within = "1d"; # Keep all archives from the last day
+                daily = 7;
+                weekly = 4;
+                monthly = -1;  # Keep at least one archive for each month
+              }
+            '';
+          };
+
+          prune.prefix = mkOption {
+            type = types.str;
+            description = ''
+              Only consider archive names starting with this prefix for pruning.
+              By default, only archives created by this job are considered.
+              Use <literal>""</literal> to consider all archives.
+            '';
+            default = config.archiveBaseName;
+            defaultText = "\${archiveBaseName}";
+          };
+
+          environment = mkOption {
+            type = with types; attrsOf str;
+            description = ''
+              Environment variables passed to the backup script.
+              You can for example specify which SSH key to use.
+            '';
+            default = { };
+            example = { BORG_RSH = "ssh -i /path/to/key"; };
+          };
+
+          preHook = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run before the backup.
+              This can for example be used to mount file systems.
+            '';
+            default = "";
+            example = ''
+              # To add excluded paths at runtime
+              extraCreateArgs="$extraCreateArgs --exclude /some/path"
+            '';
+          };
+
+          postInit = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg init</command>.
+            '';
+            default = "";
+          };
+
+          postCreate = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg create</command>. The name
+              of the created archive is stored in <literal>$archiveName</literal>.
+            '';
+            default = "";
+          };
+
+          postPrune = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg prune</command>.
+            '';
+            default = "";
+          };
+
+          postHook = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run just before exit. They are executed
+              even if a previous command exits with a non-zero exit code.
+              The latter is available as <literal>$exitStatus</literal>.
+            '';
+            default = "";
+          };
+
+          extraInitArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg init</command>.
+              Can also be set at runtime using <literal>$extraInitArgs</literal>.
+            '';
+            default = "";
+            example = "--append-only";
+          };
+
+          extraCreateArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg create</command>.
+              Can also be set at runtime using <literal>$extraCreateArgs</literal>.
+            '';
+            default = "";
+            example = "--stats --checkpoint-interval 600";
+          };
+
+          extraPruneArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg prune</command>.
+              Can also be set at runtime using <literal>$extraPruneArgs</literal>.
+            '';
+            default = "";
+            example = "--save-space";
+          };
+
+        };
+      }
+    ));
+  };
+
+  options.services.borgbackup.repos = mkOption {
+    description = ''
+      Serve BorgBackup repositories to given public SSH keys,
+      restricting their access to the repository only.
+      Also, clients do not need to specify the absolute path when accessing the repository,
+      i.e. <literal>user@machine:.</literal> is enough. (Note colon and dot.)
+    '';
+    default = { };
+    type = types.attrsOf (types.submodule (
+      { name, config, ... }: {
+        options = {
+          
+          path = mkOption {
+            type = types.path;
+            description = ''
+              Where to store the backups. Note that the directory
+              is created automatically, with correct permissions.
+            '';
+            default = "/var/lib/borgbackup";
+          };
+
+          user = mkOption {
+            type = types.str;
+            description = ''
+              The user <command>borg serve</command> is run as.
+              User or group needs write permission
+              for the specified <option>path</option>.
+            '';
+            default = "borg";
+          };
+
+          group = mkOption {
+            type = types.str;
+            description = ''
+              The group <command>borg serve</command> is run as.
+              User or group needs write permission
+              for the specified <option>path</option>.
+            '';
+            default = "borg";
+          };
+
+          authorizedKeys = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Public SSH keys that are given full write access to this repository.
+              You should use a different SSH key for each repository you write to, because
+              the specified keys are restricted to running <command>borg serve</command>
+              and can only access this single repository.
+            '';
+            default = [ ];
+          };
+
+          authorizedKeysAppendOnly = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Public SSH keys that can only be used to append new data (archives) to the repository.
+              Note that archives can still be marked as deleted and are subsequently removed from disk
+              upon accessing the repo with full write access, e.g. when pruning.
+            '';
+            default = [ ];
+          };
+
+          allowSubRepos = mkOption {
+            type = types.bool;
+            description = ''
+              Allow clients to create repositories in subdirectories of the
+              specified <option>path</option>. These can be accessed using
+              <literal>user@machine:path/to/subrepo</literal>. Note that a
+              <option>quota</option> applies to repositories independently.
+              Therefore, if this is enabled, clients can create multiple
+              repositories and upload an arbitrary amount of data.
+            '';
+            default = false;
+          };
+
+          quota = mkOption {
+            # See the definition of parse_file_size() in src/borg/helpers/parseformat.py
+            type = with types; nullOr (strMatching "[[:digit:].]+[KMGTP]?");
+            description = ''
+              Storage quota for the repository. This quota is ensured for all
+              sub-repositories if <option>allowSubRepos</option> is enabled
+              but not for the overall storage space used.
+            '';
+            default = null;
+            example = "100G";
+          };
+
+        };
+      }
+    ));
+  };
+
+  ###### implementation
+
+  config = mkIf (with config.services.borgbackup; jobs != { } || repos != { })
+    (with config.services.borgbackup; {
+      assertions =
+        mapAttrsToList mkPassAssertion jobs
+        ++ mapAttrsToList mkKeysAssertion repos;
+
+      system.activationScripts = mapAttrs' mkActivationScript jobs;
+
+      systemd.services =
+        # A job named "foo" is mapped to systemd.services.borgbackup-job-foo
+        mapAttrs' mkBackupService jobs
+        # A repo named "foo" is mapped to systemd.services.borgbackup-repo-foo
+        // mapAttrs' mkRepoService repos;
+
+      users = mkMerge (mapAttrsToList mkUsersConfig repos);
+
+      environment.systemPackages = with pkgs; [ borgbackup ];
+    });
+}
diff --git a/nixos/modules/services/backup/crashplan-small-business.nix b/nixos/modules/services/backup/crashplan-small-business.nix
new file mode 100644
index 000000000000..9497d8c18bb7
--- /dev/null
+++ b/nixos/modules/services/backup/crashplan-small-business.nix
@@ -0,0 +1,74 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.crashplansb;
+  crashplansb = pkgs.crashplansb.override { maxRam = cfg.maxRam; };
+  varDir = "/var/lib/crashplan";
+in
+
+with lib;
+
+{
+  options = {
+    services.crashplansb = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Starts crashplan for small business background service.
+        '';
+      };
+      maxRam = mkOption {
+        default = "1024m";
+        example = "2G";
+        type = types.str;
+        description = ''
+          Maximum amount of ram that the crashplan engine should use.
+        '';
+      };
+      openPorts = mkOption {
+        description = "Open ports in the firewall for crashplan.";
+        default = true;
+        type = types.bool;
+      };
+      ports =  mkOption {
+        # https://support.code42.com/Administrator/6/Planning_and_installing/TCP_and_UDP_ports_used_by_the_Code42_platform
+        # used ports can also be checked in the desktop app console using the command connection.info
+        description = "which ports to open.";
+        default = [ 4242 4243 4244 4247 ];
+        type = types.listOf types.int;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ crashplansb ];
+    networking.firewall.allowedTCPPorts = mkIf cfg.openPorts cfg.ports;
+
+    systemd.services.crashplansb = {
+      description = "CrashPlan Backup Engine";
+
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network.target" "local-fs.target" ];
+
+      preStart = ''
+        install -d -m 755 ${crashplansb.vardir}
+        install -d -m 700 ${crashplansb.vardir}/conf
+        install -d -m 700 ${crashplansb.manifestdir}
+        install -d -m 700 ${crashplansb.vardir}/cache
+        install -d -m 700 ${crashplansb.vardir}/backupArchives
+        install -d -m 777 ${crashplansb.vardir}/log
+        cp -avn ${crashplansb}/conf.template/* ${crashplansb.vardir}/conf
+      '';
+
+      serviceConfig = {
+        Type = "forking";
+        EnvironmentFile = "${crashplansb}/bin/run.conf";
+        ExecStart = "${crashplansb}/bin/CrashPlanEngine start";
+        ExecStop = "${crashplansb}/bin/CrashPlanEngine stop";
+        PIDFile = "${crashplansb.vardir}/CrashPlanEngine.pid";
+        WorkingDirectory = crashplansb;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/backup/duplicati.nix b/nixos/modules/services/backup/duplicati.nix
new file mode 100644
index 000000000000..9772ca4d20a7
--- /dev/null
+++ b/nixos/modules/services/backup/duplicati.nix
@@ -0,0 +1,40 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.duplicati;
+in
+{
+  options = {
+    services.duplicati = {
+      enable = mkEnableOption "Duplicati";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.duplicati ];
+
+    systemd.services.duplicati = {
+      description = "Duplicati backup";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "duplicati";
+        Group = "duplicati";
+        ExecStart = "${pkgs.duplicati}/bin/duplicati-server --webservice-interface=any --webservice-port=8200 --server-datafolder=/var/lib/duplicati";
+        Restart = "on-failure";
+      };
+    };
+
+    users.extraUsers.duplicati = {
+      uid = config.ids.uids.duplicati;
+      home = "/var/lib/duplicati";
+      createHome = true;
+      group = "duplicati";
+    };
+    users.extraGroups.duplicati.gid = config.ids.gids.duplicati;
+
+  };
+}
+
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
new file mode 100644
index 000000000000..21d82469c605
--- /dev/null
+++ b/nixos/modules/services/backup/restic.nix
@@ -0,0 +1,150 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  options.services.restic.backups = mkOption {
+    description = ''
+      Periodic backups to create with Restic.
+    '';
+    type = types.attrsOf (types.submodule ({ name, config, ... }: {
+      options = {
+        passwordFile = mkOption {
+          type = types.str;
+          description = ''
+            Read the repository password from a file.
+          '';
+          example = "/etc/nixos/restic-password";
+
+        };
+
+        repository = mkOption {
+          type = types.str;
+          description = ''
+            repository to backup to.
+          '';
+          example = "sftp:backup@192.168.1.100:/backups/${name}";
+        };
+
+        paths = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          description = ''
+            Which paths to backup.
+          '';
+          example = [
+            "/var/lib/postgresql"
+            "/home/user/backup"
+          ];
+        };
+
+        timerConfig = mkOption {
+          type = types.attrsOf types.str;
+          default = {
+            OnCalendar = "daily";
+          };
+          description = ''
+            When to run the backup. See man systemd.timer for details.
+          '';
+          example = {
+            OnCalendar = "00:05";
+            RandomizedDelaySec = "5h";
+          };
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "root";
+          description = ''
+            As which user the backup should run.
+          '';
+          example = "postgresql";
+        };
+
+        extraBackupArgs = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          description = ''
+            Extra arguments passed to restic backup.
+          '';
+          example = [
+            "--exclude-file=/etc/nixos/restic-ignore"
+          ];
+        };
+
+        extraOptions = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          description = ''
+            Extra extended options to be passed to the restic --option flag.
+          '';
+          example = [
+            "sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'"
+          ];
+        };
+
+        initialize = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Create the repository if it doesn't exist.
+          '';
+        };
+      };
+    }));
+    default = {};
+    example = {
+      localbackup = {
+        paths = [ "/home" ];
+        repository = "/mnt/backup-hdd";
+        passwordFile = "/etc/nixos/secrets/restic-password";
+        initialize = true;
+      };
+      remotebackup = {
+        paths = [ "/home" ];
+        repository = "sftp:backup@host:/backups/home";
+        passwordFile = "/etc/nixos/secrets/restic-password";
+        extraOptions = [
+          "sftp.command='ssh backup@host -i /etc/nixos/secrets/backup-private-key -s sftp'"
+        ];
+        timerConfig = {
+          OnCalendar = "00:05";
+          RandomizedDelaySec = "5h";
+        };
+      };
+    };
+  };
+
+  config = {
+    systemd.services =
+      mapAttrs' (name: backup:
+        let
+          extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
+          connectTo = elemAt (splitString ":" backup.repository) 1;
+          resticCmd = "${pkgs.restic}/bin/restic${extraOptions}";
+        in nameValuePair "restic-backups-${name}" ({
+          environment = {
+            RESTIC_PASSWORD_FILE = backup.passwordFile;
+            RESTIC_REPOSITORY = backup.repository;
+          };
+          path = with pkgs; [
+            openssh
+          ];
+          restartIfChanged = false;
+          serviceConfig = {
+            Type = "oneshot";
+            ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}";
+            User = backup.user;
+          };
+        } // optionalAttrs backup.initialize {
+          preStart = ''
+            ${resticCmd} snapshots || ${resticCmd} init
+          '';
+        })
+      ) config.services.restic.backups;
+    systemd.timers =
+      mapAttrs' (name: backup: nameValuePair "restic-backups-${name}" {
+        wantedBy = [ "timers.target" ];
+        timerConfig = backup.timerConfig;
+      }) config.services.restic.backups;
+  };
+}
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
index 7c9dedb67ad2..4fc7c24813a5 100644
--- a/nixos/modules/services/backup/tarsnap.nix
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -115,7 +115,7 @@ in
                 description = ''
                   Print global archive statistics upon completion.
                   The output is available via
-                  <command>systemctl status tarsnap@archive-name</command>.
+                  <command>systemctl status tarsnap-archive-name</command>.
                 '';
               };
 
@@ -238,6 +238,20 @@ in
                   Whether to produce verbose logging output.
                 '';
               };
+              explicitSymlinks = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to follow symlinks specified as archives.
+                '';
+              };
+              followSymlinks = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to follow all symlinks in archive trees.
+                '';
+              };
             };
           }
         ));
@@ -285,12 +299,12 @@ in
         }) gcfg.archives);
 
     systemd.services =
-      mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
+      (mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
         description = "Tarsnap archive '${name}'";
         requires    = [ "network-online.target" ];
         after       = [ "network-online.target" ];
 
-        path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ];
+        path = with pkgs; [ iputils tarsnap utillinux ];
 
         # In order for the persistent tarsnap timer to work reliably, we have to
         # make sure that the tarsnap server is reachable after systemd starts up
@@ -300,10 +314,12 @@ in
           while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done
         '';
 
-        script =
-          let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \
-                        -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
+        script = let
+          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          run = ''${tarsnap} -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
                         ${optionalString cfg.verbose "-v"} \
+                        ${optionalString cfg.explicitSymlinks "-H"} \
+                        ${optionalString cfg.followSymlinks "-L"} \
                         ${concatStringsSep " " cfg.directories}'';
           in if (cfg.cachedir != null) then ''
             mkdir -p ${cfg.cachedir}
@@ -313,7 +329,7 @@ in
               if [ ! -e ${cfg.cachedir}/firstrun ]; then
                 ( flock 10
                   flock -u 9
-                  tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck
+                  ${tarsnap} --fsck
                   flock 9
                 ) 10>${cfg.cachedir}/firstrun
               fi
@@ -329,7 +345,44 @@ in
           CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
           PermissionsStartOnly = "true";
         };
-      }) gcfg.archives;
+      }) gcfg.archives) //
+
+      (mapAttrs' (name: cfg: nameValuePair "tarsnap-restore-${name}"{
+        description = "Tarsnap restore '${name}'";
+        requires    = [ "network-online.target" ];
+
+        path = with pkgs; [ iputils tarsnap utillinux ];
+
+        script = let
+          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          lastArchive = ''$(${tarsnap} --list-archives | sort | tail -1)'';
+          run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}'';
+
+        in if (cfg.cachedir != null) then ''
+          mkdir -p ${cfg.cachedir}
+          chmod 0700 ${cfg.cachedir}
+
+          ( flock 9
+            if [ ! -e ${cfg.cachedir}/firstrun ]; then
+              ( flock 10
+                flock -u 9
+                ${tarsnap} --fsck
+                flock 9
+              ) 10>${cfg.cachedir}/firstrun
+            fi
+          ) 9>${cfg.cachedir}/lockf
+
+           exec flock ${cfg.cachedir}/firstrun ${run}
+        '' else "exec ${run}";
+
+        serviceConfig = {
+          Type = "oneshot";
+          IOSchedulingClass = "idle";
+          NoNewPrivileges = "true";
+          CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
+          PermissionsStartOnly = "true";
+        };
+      }) gcfg.archives);
 
     # Note: the timer must be Persistent=true, so that systemd will start it even
     # if e.g. your laptop was asleep while the latest interval occurred.
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index baf99930e3eb..3d133f82d204 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -1,39 +1,372 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with types;
 
 let
+
+  # Converts a plan like
+  #   { "1d" = "1h"; "1w" = "1d"; }
+  # into
+  #   "1d=>1h,1w=>1d"
+  attrToPlan = attrs: concatStringsSep "," (builtins.attrValues (
+    mapAttrs (n: v: "${n}=>${v}") attrs));
+
+  planDescription = ''
+      The znapzend backup plan to use for the source.
+    </para>
+    <para>
+      The plan specifies how often to backup and for how long to keep the
+      backups. It consists of a series of retention periodes to interval
+      associations:
+    </para>
+    <para>
+      <literal>
+        retA=>intA,retB=>intB,...
+      </literal>
+    </para>
+    <para>
+    Both intervals and retention periods are expressed in standard units
+    of time or multiples of them. You can use both the full name or a
+    shortcut according to the following listing:
+    </para>
+    <para>
+      <literal>
+        second|sec|s, minute|min, hour|h, day|d, week|w, month|mon|m, year|y
+      </literal>
+    </para>
+    <para>
+      See <citerefentry><refentrytitle>znapzendzetup</refentrytitle><manvolnum>1</manvolnum></citerefentry> for more info.
+  '';
+  planExample = "1h=>10min,1d=>1h,1w=>1d,1m=>1w,1y=>1m";
+
+  # A type for a string of the form number{b|k|M|G}
+  mbufferSizeType = str // {
+    check = x: str.check x && builtins.isList (builtins.match "^[0-9]+[bkMG]$" x);
+    description = "string of the form number{b|k|M|G}";
+  };
+
+  # Type for a string that must contain certain other strings (the list parameter).
+  # Note that these would need regex escaping.
+  stringContainingStrings = list: let
+    matching = s: map (str: builtins.match ".*${str}.*" s) list;
+  in str // {
+    check = x: str.check x && all isList (matching x);
+    description = "string containing all of the characters ${concatStringsSep ", " list}";
+  };
+
+  timestampType = stringContainingStrings [ "%Y" "%m" "%d" "%H" "%M" "%S" ];
+
+  destType = srcConfig: submodule ({ name, ... }: {
+    options = {
+
+      label = mkOption {
+        type = str;
+        description = "Label for this destination. Defaults to the attribute name.";
+      };
+
+      plan = mkOption {
+        type = str;
+        description = planDescription;
+        example = planExample;
+      };
+
+      dataset = mkOption {
+        type = str;
+        description = "Dataset name to send snapshots to.";
+        example = "tank/main";
+      };
+
+      host = mkOption {
+        type = nullOr str;
+        description = ''
+          Host to use for the destination dataset. Can be prefixed with
+          <literal>user@</literal> to specify the ssh user.
+        '';
+        default = null;
+        example = "john@example.com";
+      };
+
+      presend = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run before sending the snapshot to the destination.
+          Intended to run a remote script via <command>ssh</command> on the
+          destination, e.g. to bring up a backup disk or server or to put a
+          zpool online/offline. See also <option>postsend</option>.
+        '';
+        default = null;
+        example = "ssh root@bserv zpool import -Nf tank";
+      };
+
+      postsend = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run after sending the snapshot to the destination.
+          Intended to run a remote script via <command>ssh</command> on the
+          destination, e.g. to bring up a backup disk or server or to put a
+          zpool online/offline. See also <option>presend</option>.
+        '';
+        default = null;
+        example = "ssh root@bserv zpool export tank";
+      };
+    };
+
+    config = {
+      label = mkDefault name;
+      plan = mkDefault srcConfig.plan;
+    };
+  });
+
+
+
+  srcType = submodule ({ name, config, ... }: {
+    options = {
+
+      enable = mkOption {
+        type = bool;
+        description = "Whether to enable this source.";
+        default = true;
+      };
+
+      recursive = mkOption {
+        type = bool;
+        description = "Whether to do recursive snapshots.";
+        default = false;
+      };
+
+      mbuffer = {
+        enable = mkOption {
+          type = bool;
+          description = "Whether to use <command>mbuffer</command>.";
+          default = false;
+        };
+
+        port = mkOption {
+          type = nullOr ints.u16;
+          description = ''
+              Port to use for <command>mbuffer</command>.
+            </para>
+            <para>
+              If this is null, it will run <command>mbuffer</command> through
+              ssh.
+            </para>
+            <para>
+              If this is not null, it will run <command>mbuffer</command>
+              directly through TCP, which is not encrypted but faster. In that
+              case the given port needs to be open on the destination host.
+          '';
+          default = null;
+        };
+
+        size = mkOption {
+          type = mbufferSizeType;
+          description = ''
+            The size for <command>mbuffer</command>.
+            Supports the units b, k, M, G.
+          '';
+          default = "1G";
+          example = "128M";
+        };
+      };
+
+      presnap = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run before snapshots are taken on the source dataset,
+          e.g. for database locking/flushing. See also
+          <option>postsnap</option>.
+        '';
+        default = null;
+        example = literalExample ''
+          ''${pkgs.mariadb}/bin/mysql -e "set autocommit=0;flush tables with read lock;\\! ''${pkgs.coreutils}/bin/sleep 600" &  ''${pkgs.coreutils}/bin/echo $! > /tmp/mariadblock.pid ; sleep 10
+        '';
+      };
+
+      postsnap = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run after snapshots are taken on the source dataset,
+          e.g. for database unlocking. See also <option>presnap</option>.
+        '';
+        default = null;
+        example = literalExample ''
+          ''${pkgs.coreutils}/bin/kill `''${pkgs.coreutils}/bin/cat /tmp/mariadblock.pid`;''${pkgs.coreutils}/bin/rm /tmp/mariadblock.pid
+        '';
+      };
+
+      timestampFormat = mkOption {
+        type = timestampType;
+        description = ''
+          The timestamp format to use for constructing snapshot names.
+          The syntax is <literal>strftime</literal>-like. The string must
+          consist of the mandatory <literal>%Y %m %d %H %M %S</literal>.
+          Optionally  <literal>- _ . :</literal>  characters as well as any
+          alphanumeric character are allowed. If suffixed by a
+          <literal>Z</literal>, times will be in UTC.
+        '';
+        default = "%Y-%m-%d-%H%M%S";
+        example = "znapzend-%m.%d.%Y-%H%M%SZ";
+      };
+
+      sendDelay = mkOption {
+        type = int;
+        description = ''
+          Specify delay (in seconds) before sending snaps to the destination.
+          May be useful if you want to control sending time.
+        '';
+        default = 0;
+        example = 60;
+      };
+
+      plan = mkOption {
+        type = str;
+        description = planDescription;
+        example = planExample;
+      };
+
+      dataset = mkOption {
+        type = str;
+        description = "The dataset to use for this source.";
+        example = "tank/home";
+      };
+
+      destinations = mkOption {
+        type = loaOf (destType config);
+        description = "Additional destinations.";
+        default = {};
+        example = literalExample ''
+          {
+            local = {
+              dataset = "btank/backup";
+              presend = "zpool import -N btank";
+              postsend = "zpool export btank";
+            };
+            remote = {
+              host = "john@example.com";
+              dataset = "tank/john";
+            };
+          };
+        '';
+      };
+    };
+
+    config = {
+      dataset = mkDefault name;
+    };
+
+  });
+
+  ### Generating the configuration from here
+
   cfg = config.services.znapzend;
+
+  onOff = b: if b then "on" else "off";
+  nullOff = b: if isNull b then "off" else toString b;
+  stripSlashes = replaceStrings [ "/" ] [ "." ];
+
+  attrsToFile = config: concatStringsSep "\n" (builtins.attrValues (
+    mapAttrs (n: v: "${n}=${v}") config));
+
+  mkDestAttrs = dst: with dst;
+    mapAttrs' (n: v: nameValuePair "dst_${label}${n}" v) ({
+      "" = optionalString (! isNull host) "${host}:" + dataset;
+      _plan = plan;
+    } // optionalAttrs (presend != null) {
+      _precmd = presend;
+    } // optionalAttrs (postsend != null) {
+      _pstcmd = postsend;
+    });
+
+  mkSrcAttrs = srcCfg: with srcCfg; {
+    enabled = onOff enable;
+    mbuffer = with mbuffer; if enable then "${pkgs.mbuffer}/bin/mbuffer"
+        + optionalString (port != null) ":${toString port}" else "off";
+    mbuffer_size = mbuffer.size;
+    post_znap_cmd = nullOff postsnap;
+    pre_znap_cmd = nullOff presnap;
+    recursive = onOff recursive;
+    src = dataset;
+    src_plan = plan;
+    tsformat = timestampFormat;
+    zend_delay = toString sendDelay;
+  } // fold (a: b: a // b) {} (
+    map mkDestAttrs (builtins.attrValues destinations)
+  );
+
+  files = mapAttrs' (n: srcCfg: let
+    fileText = attrsToFile (mkSrcAttrs srcCfg);
+  in {
+    name = srcCfg.dataset;
+    value = pkgs.writeText (stripSlashes srcCfg.dataset) fileText;
+  }) cfg.zetup;
+
 in
 {
   options = {
     services.znapzend = {
-      enable = mkEnableOption "ZnapZend daemon";
+      enable = mkEnableOption "ZnapZend ZFS backup daemon";
 
       logLevel = mkOption {
         default = "debug";
         example = "warning";
-        type = lib.types.enum ["debug" "info" "warning" "err" "alert"];
-        description = "The log level when logging to file. Any of debug, info, warning, err, alert. Default in daemonized form is debug.";
+        type = enum ["debug" "info" "warning" "err" "alert"];
+        description = ''
+          The log level when logging to file. Any of debug, info, warning, err,
+          alert. Default in daemonized form is debug.
+        '';
       };
 
       logTo = mkOption {
-        type = types.str;
+        type = str;
         default = "syslog::daemon";
         example = "/var/log/znapzend.log";
-        description = "Where to log to (syslog::&lt;facility&gt; or &lt;filepath&gt;).";
+        description = ''
+          Where to log to (syslog::&lt;facility&gt; or &lt;filepath&gt;).
+        '';
       };
 
       noDestroy = mkOption {
-        type = types.bool;
+        type = bool;
         default = false;
         description = "Does all changes to the filesystem except destroy.";
       };
 
       autoCreation = mkOption {
-        type = types.bool;
+        type = bool;
+        default = false;
+        description = "Automatically create the destination dataset if it does not exists.";
+      };
+
+      zetup = mkOption {
+        type = loaOf srcType;
+        description = "Znapzend configuration.";
+        default = {};
+        example = literalExample ''
+          {
+            "tank/home" = {
+              # Make snapshots of tank/home every hour, keep those for 1 day,
+              # keep every days snapshot for 1 month, etc.
+              plan = "1d=>1h,1m=>1d,1y=>1m";
+              recursive = true;
+              # Send all those snapshots to john@example.com:rtank/john as well
+              destinations.remote = {
+                host = "john@example.com";
+                dataset = "rtank/john";
+              };
+            };
+          };
+        '';
+      };
+
+      pure = mkOption {
+        type = bool;
+        description = ''
+          Do not persist any stateful znapzend setups. If this option is
+          enabled, your previously set znapzend setups will be cleared and only
+          the ones defined with this module will be applied.
+        '';
         default = false;
-        description = "Automatically create the dataset on dest if it does not exists.";
       };
     };
   };
@@ -49,12 +382,30 @@ in
 
         path = with pkgs; [ zfs mbuffer openssh ];
 
+        preStart = optionalString cfg.pure ''
+          echo Resetting znapzend zetups
+          ${pkgs.znapzend}/bin/znapzendzetup list \
+            | grep -oP '(?<=\*\*\* backup plan: ).*(?= \*\*\*)' \
+            | xargs -I{} ${pkgs.znapzend}/bin/znapzendzetup delete "{}"
+        '' + concatStringsSep "\n" (mapAttrsToList (dataset: config: ''
+          echo Importing znapzend zetup ${config} for dataset ${dataset}
+          ${pkgs.znapzend}/bin/znapzendzetup import --write ${dataset} ${config}
+        '') files);
+
         serviceConfig = {
-          ExecStart = "${pkgs.znapzend}/bin/znapzend --logto=${cfg.logTo} --loglevel=${cfg.logLevel} ${optionalString cfg.noDestroy "--nodestroy"} ${optionalString cfg.autoCreation "--autoCreation"}";
+          ExecStart = let
+            args = concatStringsSep " " [
+              "--logto=${cfg.logTo}"
+              "--loglevel=${cfg.logLevel}"
+              (optionalString cfg.noDestroy "--nodestroy")
+              (optionalString cfg.autoCreation "--autoCreation")
+            ]; in "${pkgs.znapzend}/bin/znapzend ${args}";
           ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
           Restart = "on-failure";
         };
       };
     };
   };
+
+  meta.maintainers = with maintainers; [ infinisil ];
 }
diff --git a/nixos/modules/services/cluster/kubernetes/dashboard.nix b/nixos/modules/services/cluster/kubernetes/dashboard.nix
index 75d71fccfda4..e331889b9dd5 100644
--- a/nixos/modules/services/cluster/kubernetes/dashboard.nix
+++ b/nixos/modules/services/cluster/kubernetes/dashboard.nix
@@ -6,12 +6,12 @@ let
   cfg = config.services.kubernetes.addons.dashboard;
 
   name = "gcr.io/google_containers/kubernetes-dashboard-amd64";
-	version = "v1.6.3";
+	version = "v1.8.2";
 
   image = pkgs.dockerTools.pullImage {
     imageName = name;
     imageTag = version;
-    sha256 = "1sf54d96nkgic9hir9c6p14gw24ns1k5d5a0r1sg414kjrvic0b4";
+    sha256 = "11h0fz3wxp0f10fsyqaxjm7l2qg7xws50dv5iwlck5gb1fjmajad";
   };
 in {
   options.services.kubernetes.addons.dashboard = {
diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix
index 077953e4d4f8..aeb0a0d2432d 100644
--- a/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixos/modules/services/cluster/kubernetes/default.nix
@@ -279,7 +279,7 @@ in {
       tokenAuthFile = mkOption {
         description = ''
           Kubernetes apiserver token authentication file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/>
         '';
         default = null;
         type = types.nullOr types.path;
@@ -288,7 +288,7 @@ in {
       basicAuthFile = mkOption {
         description = ''
           Kubernetes apiserver basic authentication file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/>
         '';
         default = pkgs.writeText "users" ''
           kubernetes,admin,0
@@ -299,16 +299,16 @@ in {
       authorizationMode = mkOption {
         description = ''
           Kubernetes apiserver authorization mode (AlwaysAllow/AlwaysDeny/ABAC/RBAC). See
-          <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/>
         '';
-        default = ["RBAC"];
-        type = types.listOf (types.enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "RBAC"]);
+        default = ["RBAC" "Node"];
+        type = types.listOf (types.enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "RBAC" "Node"]);
       };
 
       authorizationPolicy = mkOption {
         description = ''
           Kubernetes apiserver authorization policy file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/>
         '';
         default = [];
         type = types.listOf types.attrs;
@@ -332,7 +332,7 @@ in {
       runtimeConfig = mkOption {
         description = ''
           Api runtime configuration. See
-          <link xlink:href="http://kubernetes.io/docs/admin/cluster-management.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/cluster-management.html"/>
         '';
         default = "authentication.k8s.io/v1beta1=true";
         example = "api/all=false,api/v1=true";
@@ -342,9 +342,9 @@ in {
       admissionControl = mkOption {
         description = ''
           Kubernetes admission control plugins to use. See
-          <link xlink:href="http://kubernetes.io/docs/admin/admission-controllers/"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/admission-controllers/"/>
         '';
-        default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds"];
+        default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds" "NodeRestriction"];
         example = [
           "NamespaceLifecycle" "NamespaceExists" "LimitRanger"
           "SecurityContextDeny" "ServiceAccount" "ResourceQuota"
@@ -766,7 +766,7 @@ in {
           rm /opt/cni/bin/* || true
           ${concatMapStrings (package: ''
             echo "Linking cni package: ${package}"
-            ln -fs ${package.plugins}/* /opt/cni/bin
+            ln -fs ${package}/bin/* /opt/cni/bin
           '') cfg.kubelet.cni.packages}
         '';
         serviceConfig = {
@@ -828,7 +828,7 @@ in {
       };
 
       # Allways include cni plugins
-      services.kubernetes.kubelet.cni.packages = [pkgs.cni];
+      services.kubernetes.kubelet.cni.packages = [pkgs.cni-plugins];
 
       boot.kernelModules = ["br_netfilter"];
 
diff --git a/nixos/modules/services/computing/slurm/slurm.nix b/nixos/modules/services/computing/slurm/slurm.nix
index fb91a29a4000..45d34f5b76f5 100644
--- a/nixos/modules/services/computing/slurm/slurm.nix
+++ b/nixos/modules/services/computing/slurm/slurm.nix
@@ -6,14 +6,20 @@ let
 
   cfg = config.services.slurm;
   # configuration file can be generated by http://slurm.schedmd.com/configurator.html
-  configFile = pkgs.writeText "slurm.conf" 
+  configFile = pkgs.writeText "slurm.conf"
     ''
       ${optionalString (cfg.controlMachine != null) ''controlMachine=${cfg.controlMachine}''}
       ${optionalString (cfg.controlAddr != null) ''controlAddr=${cfg.controlAddr}''}
       ${optionalString (cfg.nodeName != null) ''nodeName=${cfg.nodeName}''}
       ${optionalString (cfg.partitionName != null) ''partitionName=${cfg.partitionName}''}
+      PlugStackConfig=${plugStackConfig}
       ${cfg.extraConfig}
     '';
+
+  plugStackConfig = pkgs.writeText "plugstack.conf"
+    ''
+      ${optionalString cfg.enableSrunX11 ''optional ${pkgs.slurm-spank-x11}/lib/x11.so''}
+    '';
 in
 
 {
@@ -28,7 +34,7 @@ in
         enable = mkEnableOption "slurm control daemon";
 
       };
-      
+
       client = {
         enable = mkEnableOption "slurm rlient daemon";
 
@@ -86,8 +92,19 @@ in
         '';
       };
 
+      enableSrunX11 = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled srun will accept the option "--x11" to allow for X11 forwarding
+          from within an interactive session or a batch job. This activates the
+          slurm-spank-x11 module. Note that this requires 'services.openssh.forwardX11'
+          to be enabled on the compute nodes.
+        '';
+      };
+
       extraConfig = mkOption {
-        default = ""; 
+        default = "";
         type = types.lines;
         description = ''
           Extra configuration options that will be added verbatim at
@@ -134,7 +151,8 @@ in
     environment.systemPackages = [ wrappedSlurm ];
 
     systemd.services.slurmd = mkIf (cfg.client.enable) {
-      path = with pkgs; [ wrappedSlurm coreutils ];
+      path = with pkgs; [ wrappedSlurm coreutils ]
+        ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
 
       wantedBy = [ "multi-user.target" ];
       after = [ "systemd-tmpfiles-clean.service" ];
@@ -152,8 +170,9 @@ in
     };
 
     systemd.services.slurmctld = mkIf (cfg.server.enable) {
-      path = with pkgs; [ wrappedSlurm munge coreutils ];
-      
+      path = with pkgs; [ wrappedSlurm munge coreutils ]
+        ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
+
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" "munged.service" ];
       requires = [ "munged.service" ];
diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix
index 9c06e1d43bbe..03af9a7859ec 100644
--- a/nixos/modules/services/continuous-integration/buildkite-agent.nix
+++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix
@@ -4,14 +4,31 @@ 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="${cfg.package}/share/hooks"
-      build-path="${cfg.dataDir}"
+
+  mkHookOption = { name, description, example ? null }: {
+    inherit name;
+    value = mkOption {
+      default = null;
+      inherit description;
+      type = types.nullOr types.lines;
+    } // (if example == null then {} else { inherit example; });
+  };
+  mkHookOptions = hooks: listToAttrs (map mkHookOption hooks);
+
+  hooksDir = let
+    mkHookEntry = name: value: ''
+      cat > $out/${name} <<EOF
+      #! ${pkgs.runtimeShell}
+      set -e
+      ${value}
+      EOF
+      chmod 755 $out/${name}
     '';
+  in pkgs.runCommand "buildkite-agent-hooks" {} ''
+    mkdir $out
+    ${concatStringsSep "\n" (mapAttrsToList mkHookEntry (filterAttrs (n: v: v != null) cfg.hooks))}
+  '';
+
 in
 
 {
@@ -39,15 +56,19 @@ in
         type = types.listOf types.package;
       };
 
-      token = mkOption {
-        type = types.str;
+      tokenPath = mkOption {
+        type = types.path;
         description = ''
           The token from your Buildkite "Agents" page.
+
+          A run-time path to the token file, which is supposed to be provisioned
+          outside of Nix store.
         '';
       };
 
       name = mkOption {
         type = types.str;
+        default = "%hostname-%n";
         description = ''
           The name of the agent.
         '';
@@ -56,25 +77,110 @@ in
       meta-data = mkOption {
         type = types.str;
         default = "";
+        example = "queue=default,docker=true,ruby2=true";
+        description = ''
+          Meta data for the agent. This is a comma-separated list of
+          <code>key=value</code> pairs.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = "debug=true";
         description = ''
-          Meta data for the agent.
+          Extra lines to be added verbatim to the configuration file.
         '';
       };
 
       openssh =
-        { privateKey = mkOption {
-            type = types.str;
+        { privateKeyPath = mkOption {
+            type = types.path;
             description = ''
               Private agent key.
+
+              A run-time path to the key file, which is supposed to be provisioned
+              outside of Nix store.
             '';
           };
-          publicKey = mkOption {
-            type = types.str;
+          publicKeyPath = mkOption {
+            type = types.path;
             description = ''
               Public agent key.
+
+              A run-time path to the key file, which is supposed to be provisioned
+              outside of Nix store.
             '';
           };
         };
+
+      hooks = mkHookOptions [
+        { name = "checkout";
+          description = ''
+            The `checkout` hook script will replace the default checkout routine of the
+            bootstrap.sh script. You can use this hook to do your own SCM checkout
+            behaviour
+          ''; }
+        { name = "command";
+          description = ''
+            The `command` hook script will replace the default implementation of running
+            the build command.
+          ''; }
+        { name = "environment";
+          description = ''
+            The `environment` hook will run before all other commands, and can be used
+            to set up secrets, data, etc. Anything exported in hooks will be available
+            to the build script.
+
+            Note: the contents of this file will be copied to the world-readable
+            Nix store.
+          '';
+          example = ''
+            export SECRET_VAR=`head -1 /run/keys/secret`
+          ''; }
+        { name = "post-artifact";
+          description = ''
+            The `post-artifact` hook will run just after artifacts are uploaded
+          ''; }
+        { name = "post-checkout";
+          description = ''
+            The `post-checkout` hook will run after the bootstrap script has checked out
+            your projects source code.
+          ''; }
+        { name = "post-command";
+          description = ''
+            The `post-command` hook will run after the bootstrap script has run your
+            build commands
+          ''; }
+        { name = "pre-artifact";
+          description = ''
+            The `pre-artifact` hook will run just before artifacts are uploaded
+          ''; }
+        { name = "pre-checkout";
+          description = ''
+            The `pre-checkout` hook will run just before your projects source code is
+            checked out from your SCM provider
+          ''; }
+        { name = "pre-command";
+          description = ''
+            The `pre-command` hook will run just before your build command runs
+          ''; }
+        { name = "pre-exit";
+          description = ''
+            The `pre-exit` hook will run just before your build job finishes
+          ''; }
+      ];
+
+      hooksPath = mkOption {
+        type = types.path;
+        default = hooksDir;
+        defaultText = "generated from services.buildkite-agent.hooks";
+        description = ''
+          Path to the directory storing the hooks.
+          Consider using <option>services.buildkite-agent.hooks.&lt;name&gt;</option>
+          instead.
+        '';
+      };
     };
   };
 
@@ -84,6 +190,7 @@ in
         home = cfg.dataDir;
         createHome = true;
         description = "Buildkite agent user";
+        extraGroups = [ "keys" ];
       };
 
     environment.systemPackages = [ cfg.package ];
@@ -92,28 +199,54 @@ in
       { description = "Buildkite Agent";
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
-        path = cfg.runtimePackages;
+        path = cfg.runtimePackages ++ [ pkgs.coreutils ];
         environment = config.networking.proxy.envVars // {
           HOME = cfg.dataDir;
           NIX_REMOTE = "daemon";
         };
-        preStart = ''
-          ${pkgs.coreutils}/bin/mkdir -m 0700 -p ${cfg.dataDir}/.ssh
-
-          echo "${cfg.openssh.privateKey}" > ${cfg.dataDir}/.ssh/id_rsa
-          ${pkgs.coreutils}/bin/chmod 600 ${cfg.dataDir}/.ssh/id_rsa
 
-          echo "${cfg.openssh.publicKey}" > ${cfg.dataDir}/.ssh/id_rsa.pub
-          ${pkgs.coreutils}/bin/chmod 600 ${cfg.dataDir}/.ssh/id_rsa.pub
-        '';
+        ## NB: maximum care is taken so that secrets (ssh keys and the CI token)
+        ##     don't end up in the Nix store.
+        preStart = let
+          sshDir = "${cfg.dataDir}/.ssh";
+        in
+          ''
+            mkdir -m 0700 -p "${sshDir}"
+            cp -f "${toString cfg.openssh.privateKeyPath}" "${sshDir}/id_rsa"
+            cp -f "${toString cfg.openssh.publicKeyPath}"  "${sshDir}/id_rsa.pub"
+            chmod 600 "${sshDir}"/id_rsa*
+
+            cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
+            token="$(cat ${toString cfg.tokenPath})"
+            name="${cfg.name}"
+            meta-data="${cfg.meta-data}"
+            build-path="${cfg.dataDir}/builds"
+            hooks-path="${cfg.hooksPath}"
+            ${cfg.extraConfig}
+            EOF
+          '';
 
         serviceConfig =
-          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config ${configFile}";
+          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config /var/lib/buildkite-agent/buildkite-agent.cfg";
             User = "buildkite-agent";
             RestartSec = 5;
             Restart = "on-failure";
             TimeoutSec = 10;
           };
       };
+
+    assertions = [
+      { assertion = cfg.hooksPath == hooksDir || all isNull (attrValues cfg.hooks);
+        message = ''
+          Options `services.buildkite-agent.hooksPath' and
+          `services.buildkite-agent.hooks.<name>' are mutually exclusive.
+        '';
+      }
+    ];
   };
+  imports = [
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "token" ]                [ "services" "buildkite-agent" "tokenPath" ])
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "privateKey" ] [ "services" "buildkite-agent" "openssh" "privateKeyPath" ])
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "publicKey" ]  [ "services" "buildkite-agent" "openssh" "publicKeyPath" ])
+  ];
 }
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index 43fec5ff5bb2..2fa7c59a965d 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -28,6 +28,7 @@ let
 
   serverEnv = env //
     { HYDRA_TRACKER = cfg.tracker;
+      XDG_CACHE_HOME = "${baseDir}/www/.cache";
       COLUMNS = "80";
       PGPASSFILE = "${baseDir}/pgpass-www"; # grrr
     } // (optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; });
@@ -225,14 +226,14 @@ in
 
     services.hydra.extraConfig =
       ''
-        using_frontend_proxy 1
-        base_uri ${cfg.hydraURL}
-        notification_sender ${cfg.notificationSender}
-        max_servers 25
+        using_frontend_proxy = 1
+        base_uri = ${cfg.hydraURL}
+        notification_sender = ${cfg.notificationSender}
+        max_servers = 25
         ${optionalString (cfg.logo != null) ''
-          hydra_logo ${cfg.logo}
+          hydra_logo = ${cfg.logo}
         ''}
-        gc_roots_dir ${cfg.gcRootsDir}
+        gc_roots_dir = ${cfg.gcRootsDir}
         use-substitutes = ${if cfg.useSubstitutes then "1" else "0"}
       '';
 
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index 0dd59e4fb444..c2f4e9c0c5a7 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -145,6 +145,11 @@ in {
   };
 
   config = mkIf cfg.enable {
+    # server references the dejavu fonts
+    environment.systemPackages = [
+      pkgs.dejavu_fonts
+    ];
+
     users.extraGroups = optional (cfg.group == "jenkins") {
       name = "jenkins";
       gid = config.ids.gids.jenkins;
@@ -200,15 +205,17 @@ in {
           ${replacePlugins}
         '';
 
+      # For reference: https://wiki.jenkins.io/display/JENKINS/JenkinsLinuxStartupScript
       script = ''
         ${pkgs.jdk}/bin/java ${concatStringsSep " " cfg.extraJavaOptions} -jar ${cfg.package}/webapps/jenkins.war --httpListenAddress=${cfg.listenAddress} \
                                                   --httpPort=${toString cfg.port} \
                                                   --prefix=${cfg.prefix} \
+                                                  -Djava.awt.headless=true \
                                                   ${concatStringsSep " " cfg.extraOptions}
       '';
 
       postStart = ''
-        until [[ $(${pkgs.curl.bin}/bin/curl -s --head -w '\n%{http_code}' http://${cfg.listenAddress}:${toString cfg.port}${cfg.prefix} | tail -n1) =~ ^(200|403)$ ]]; do
+        until [[ $(${pkgs.curl.bin}/bin/curl -L -s --head -w '\n%{http_code}' http://${cfg.listenAddress}:${toString cfg.port}${cfg.prefix} | tail -n1) =~ ^(200|403)$ ]]; do
           sleep 1
         done
       '';
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix
index 906cb320df98..d528355671f6 100644
--- a/nixos/modules/services/databases/4store-endpoint.nix
+++ b/nixos/modules/services/databases/4store-endpoint.nix
@@ -2,7 +2,7 @@
 let
   cfg = config.services.fourStoreEndpoint;
   endpointUser = "fourstorehttp";
-  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c";
+  run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${endpointUser} -c";
 in
 with lib;
 {
diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix
index 62856822f906..abb62e1f2637 100644
--- a/nixos/modules/services/databases/4store.nix
+++ b/nixos/modules/services/databases/4store.nix
@@ -3,7 +3,7 @@ let
   cfg = config.services.fourStore;
   stateDir = "/var/lib/4store";
   fourStoreUser = "fourstore";
-  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fourStoreUser}";
+  run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fourStoreUser}";
 in
 with lib;
 {
diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix
index c6875af506d3..46bc6fc5c132 100644
--- a/nixos/modules/services/databases/memcached.nix
+++ b/nixos/modules/services/databases/memcached.nix
@@ -40,11 +40,7 @@ in
         description = "The port to bind to";
       };
 
-      socket = mkOption {
-        default = "";
-        description = "Unix socket path to listen on. Setting this will disable network support";
-        example = "/var/run/memcached";
-      };
+      enableUnixSocket = mkEnableOption "unix socket at /run/memcached/memcached.sock";
 
       maxMemory = mkOption {
         default = 64;
@@ -68,31 +64,40 @@ in
 
   config = mkIf config.services.memcached.enable {
 
-    users.extraUsers.memcached =
-      { name = cfg.user;
-        uid = config.ids.uids.memcached;
-        description = "Memcached server user";
-      };
+    users.extraUsers = optional (cfg.user == "memcached") {
+      name = "memcached";
+      description = "Memcached server user";
+    };
 
     environment.systemPackages = [ memcached ];
 
-    systemd.services.memcached =
-      { description = "Memcached server";
-
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
-
-        serviceConfig = {
-          ExecStart =
-            let
-              networking = if cfg.socket != ""
-                then "-s ${cfg.socket}"
-                else "-l ${cfg.listen} -p ${toString cfg.port}";
-            in "${memcached}/bin/memcached ${networking} -m ${toString cfg.maxMemory} -c ${toString cfg.maxConnections} ${concatStringsSep " " cfg.extraOptions}";
-
-          User = cfg.user;
-        };
+    systemd.services.memcached = {
+      description = "Memcached server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        PermissionsStartOnly = true;
+        ExecStartPre = optionals cfg.enableUnixSocket [
+          "${pkgs.coreutils}/bin/install -d -o ${cfg.user} /run/memcached/"
+          "${pkgs.coreutils}/bin/chown -R ${cfg.user} /run/memcached/"
+        ];
+        ExecStart =
+        let
+          networking = if cfg.enableUnixSocket
+          then "-s /run/memcached/memcached.sock"
+          else "-l ${cfg.listen} -p ${toString cfg.port}";
+        in "${memcached}/bin/memcached ${networking} -m ${toString cfg.maxMemory} -c ${toString cfg.maxConnections} ${concatStringsSep " " cfg.extraOptions}";
+
+        User = cfg.user;
       };
+    };
   };
+  imports = [
+    (mkRemovedOptionModule ["services" "memcached" "socket"] ''
+      This option was replaced by a fixed unix socket path at /run/memcached/memcached.sock enabled using services.memached.enableUnixSocket.
+    '')
+  ];
 
 }
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index a3bf4f9ba925..21a131b90a81 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -7,14 +7,12 @@ let
   cfg = config.services.mysql;
 
   mysql = cfg.package;
-  
-  isMariaDB = 
+
+  isMariaDB =
     let
       pName = _p: (builtins.parseDrvName (_p.name)).name;
     in pName mysql == pName pkgs.mariadb;
 
-  atLeast55 = versionAtLeast mysql.mysqlVersion "5.5";
-
   pidFile = "${cfg.pidDir}/mysqld.pid";
 
   mysqldOptions =
@@ -28,13 +26,6 @@ let
     ${optionalString (cfg.bind != null) "bind-address = ${cfg.bind}" }
     ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"}
     ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"}
-    ${optionalString (cfg.replication.role == "slave" && !atLeast55)
-    ''
-      master-host = ${cfg.replication.masterHost}
-      master-user = ${cfg.replication.masterUser}
-      master-password = ${cfg.replication.masterPassword}
-      master-port = ${toString cfg.replication.masterPort}
-    ''}
     ${optionalString (cfg.ensureUsers != [])
     ''
       plugin-load-add = auth_socket.so
@@ -142,7 +133,7 @@ in
         '';
         example = [
           "nextcloud"
-          "piwik"
+          "matomo"
         ];
       };
 
@@ -298,10 +289,10 @@ in
                     # Create initial databases
                     if ! test -e "${cfg.dataDir}/${database.name}"; then
                         echo "Creating initial database: ${database.name}"
-                        ( echo "create database ${database.name};"
+                        ( echo 'create database `${database.name}`;'
 
                           ${optionalString (database ? "schema") ''
-                          echo "use ${database.name};"
+                          echo 'use `${database.name}`;'
 
                           if [ -f "${database.schema}" ]
                           then
@@ -315,7 +306,7 @@ in
                     fi
                   '') cfg.initialDatabases}
 
-                ${optionalString (cfg.replication.role == "master" && atLeast55)
+                ${optionalString (cfg.replication.role == "master")
                   ''
                     # Set up the replication master
 
@@ -326,7 +317,7 @@ in
                     ) | ${mysql}/bin/mysql -u root -N
                   ''}
 
-                ${optionalString (cfg.replication.role == "slave" && atLeast55)
+                ${optionalString (cfg.replication.role == "slave")
                   ''
                     # Set up the replication slave
 
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index e884098cb08d..a67c61eb9949 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -7,8 +7,10 @@ let
   cfg = config.services.openldap;
   openldap = pkgs.openldap;
 
+  dataFile = pkgs.writeText "ldap-contents.ldif" cfg.declarativeContents;
   configFile = pkgs.writeText "slapd.conf" cfg.extraConfig;
-
+  configOpts = if cfg.configDir == null then "-f ${configFile}"
+               else "-F ${cfg.configDir}";
 in
 
 {
@@ -81,6 +83,34 @@ in
             '''
           '';
       };
+
+      declarativeContents = mkOption {
+        type = with types; nullOr lines;
+        default = null;
+        description = ''
+          Declarative contents for the LDAP database, in LDIF format.
+
+          Note a few facts when using it. First, the database
+          <emphasis>must</emphasis> be stored in the directory defined by
+          <code>dataDir</code>. Second, all <code>dataDir</code> will be erased
+          when starting the LDAP server. Third, modifications to the database
+          are not prevented, they are just dropped on the next reboot of the
+          server. Finally, performance-wise the database and indexes are rebuilt
+          on each server startup, so this will slow down server startup,
+          especially with large databases.
+        '';
+        example = ''
+          dn: dc=example,dc=org
+          objectClass: domain
+          dc: example
+
+          dn: ou=users,dc=example,dc=org
+          objectClass = organizationalUnit
+          ou: users
+
+          # ...
+        '';
+      };
     };
 
   };
@@ -88,7 +118,7 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.openldap.enable {
+  config = mkIf cfg.enable {
 
     environment.systemPackages = [ openldap ];
 
@@ -98,11 +128,21 @@ in
       after = [ "network.target" ];
       preStart = ''
         mkdir -p /var/run/slapd
-        chown -R ${cfg.user}:${cfg.group} /var/run/slapd
-        mkdir -p ${cfg.dataDir}
-        chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}
+        chown -R "${cfg.user}:${cfg.group}" /var/run/slapd
+        ${optionalString (cfg.declarativeContents != null) ''
+          rm -Rf "${cfg.dataDir}"
+        ''}
+        mkdir -p "${cfg.dataDir}"
+        ${optionalString (cfg.declarativeContents != null) ''
+          ${openldap.out}/bin/slapadd ${configOpts} -l ${dataFile}
+        ''}
+        chown -R "${cfg.user}:${cfg.group}" "${cfg.dataDir}"
       '';
-      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" ${if cfg.configDir == null then "-f "+configFile else "-F "+cfg.configDir}";
+      serviceConfig.ExecStart =
+        "${openldap.out}/libexec/slapd -d 0 " +
+          "-u '${cfg.user}' -g '${cfg.group}' " +
+          "-h '${concatStringsSep " " cfg.urlList}' " +
+          "${configOpts}";
     };
 
     users.extraUsers.openldap =
diff --git a/nixos/modules/services/databases/pgmanage.nix b/nixos/modules/services/databases/pgmanage.nix
index 86733a3e5a07..d1b48c06440e 100644
--- a/nixos/modules/services/databases/pgmanage.nix
+++ b/nixos/modules/services/databases/pgmanage.nix
@@ -22,7 +22,7 @@ let
 
       web_root = ${cfg.package}/etc/pgmanage/web_root
 
-      data_root = ${cfg.dataRoot}
+      sql_root = ${cfg.sqlRoot}
 
       ${optionalString (!isNull cfg.tls) ''
       tls_cert = ${cfg.tls.cert}
@@ -130,7 +130,7 @@ let
       '';
     };
 
-    dataRoot = mkOption {
+    sqlRoot = mkOption {
       type = types.str;
       default = "/var/lib/pgmanage";
       description = ''
@@ -210,7 +210,7 @@ in {
         users."${pgmanage}" = {
           name  = pgmanage;
           group = pgmanage;
-          home  = cfg.dataRoot;
+          home  = cfg.sqlRoot;
           createHome = true;
         };
         groups."${pgmanage}" = {
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 9b5e3735239f..f022e0863dfd 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -36,9 +36,6 @@ let
       ${cfg.extraConfig}
     '';
 
-  pre84 = versionOlder (builtins.parseDrvName postgresql.name).version "8.4";
-
-
 in
 
 {
@@ -122,7 +119,7 @@ in
       extraPlugins = mkOption {
         type = types.listOf types.path;
         default = [];
-        example = literalExample "[ (pkgs.postgis.override { postgresql = pkgs.postgresql94; }).v_2_1_4 ]";
+        example = literalExample "[ (pkgs.postgis.override { postgresql = pkgs.postgresql94; }) ]";
         description = ''
           When this list contains elements a new store path is created.
           PostgreSQL and the elements are symlinked into it. Then pg_config,
@@ -182,7 +179,7 @@ in
     services.postgresql.authentication = mkAfter
       ''
         # Generated file; do not edit!
-        local all all              ident ${optionalString pre84 "sameuser"}
+        local all all              ident
         host  all all 127.0.0.1/32 md5
         host  all all ::1/128      md5
       '';
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index a039ad138f6f..e4e38a4364a0 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -219,7 +219,6 @@ in
 
     users.extraUsers.redis =
       { name = cfg.user;
-        uid = config.ids.uids.redis;
         description = "Redis database user";
       };
 
diff --git a/nixos/modules/services/desktops/gnome3/at-spi2-core.nix b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
index 55ed2d9ee21b..cca98c43dc7a 100644
--- a/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
+++ b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
@@ -28,14 +28,15 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.services.gnome3.at-spi2-core.enable {
-
-    environment.systemPackages = [ pkgs.at_spi2_core ];
-
-    services.dbus.packages = [ pkgs.at_spi2_core ];
-
-    systemd.packages = [ pkgs.at_spi2_core ];
-
-  };
-
+  config = mkMerge [
+    (mkIf config.services.gnome3.at-spi2-core.enable {
+      environment.systemPackages = [ pkgs.at-spi2-core ];
+      services.dbus.packages = [ pkgs.at-spi2-core ];
+      systemd.packages = [ pkgs.at-spi2-core ];
+    })
+
+    (mkIf (!config.services.gnome3.at-spi2-core.enable) {
+      environment.variables.NO_AT_BRIDGE = "1";
+    })
+  ];
 }
diff --git a/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix
new file mode 100644
index 000000000000..2740a22c7ca0
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix
@@ -0,0 +1,27 @@
+# Chrome GNOME Shell native host connector.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.gnome3.chrome-gnome-shell.enable = mkEnableOption ''
+      Chrome GNOME Shell native host connector, a DBus service
+      allowing to install GNOME Shell extensions from a web browser.
+    '';
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.gnome3.chrome-gnome-shell.enable {
+    environment.etc = {
+      "chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json";
+      "opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json";
+    };
+
+    environment.systemPackages = [ pkgs.chrome-gnome-shell ];
+
+    services.dbus.packages = [ pkgs.chrome-gnome-shell ];
+  };
+}
diff --git a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
index 86a47488d865..7e312a1b81eb 100644
--- a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
+++ b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
@@ -30,11 +30,11 @@ with lib;
 
   config = mkIf config.services.gnome3.evolution-data-server.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.evolution_data_server ];
+    environment.systemPackages = [ pkgs.gnome3.evolution-data-server ];
 
-    services.dbus.packages = [ pkgs.gnome3.evolution_data_server ];
+    services.dbus.packages = [ pkgs.gnome3.evolution-data-server ];
 
-    systemd.packages = [ pkgs.gnome3.evolution_data_server ];
+    systemd.packages = [ pkgs.gnome3.evolution-data-server ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/gnome-keyring.nix b/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
index 2a68af5a7dd8..aa1165ab3bba 100644
--- a/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
+++ b/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
@@ -31,9 +31,9 @@ with lib;
 
   config = mkIf config.services.gnome3.gnome-keyring.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome_keyring ];
+    environment.systemPackages = [ pkgs.gnome3.gnome-keyring ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome_keyring pkgs.gnome3.gcr ];
+    services.dbus.packages = [ pkgs.gnome3.gnome-keyring pkgs.gnome3.gcr ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix b/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
index 0da4aca73ecb..4286251357f7 100644
--- a/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
+++ b/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
@@ -30,9 +30,9 @@ with lib;
 
   config = mkIf config.services.gnome3.gnome-online-accounts.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome_online_accounts ];
+    environment.systemPackages = [ pkgs.gnome3.gnome-online-accounts ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome_online_accounts ];
+    services.dbus.packages = [ pkgs.gnome3.gnome-online-accounts ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
index 3ac767bfa00d..fd14efee5f2e 100644
--- a/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
+++ b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
@@ -30,11 +30,11 @@ with lib;
 
   config = mkIf config.services.gnome3.gnome-terminal-server.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome_terminal ];
+    environment.systemPackages = [ pkgs.gnome3.gnome-terminal ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome_terminal ];
+    services.dbus.packages = [ pkgs.gnome3.gnome-terminal ];
 
-    systemd.packages = [ pkgs.gnome3.gnome_terminal ];
+    systemd.packages = [ pkgs.gnome3.gnome-terminal ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/tracker-miners.nix b/nixos/modules/services/desktops/gnome3/tracker-miners.nix
new file mode 100644
index 000000000000..20154fc2fed3
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/tracker-miners.nix
@@ -0,0 +1,41 @@
+# Tracker Miners daemons.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.tracker-miners = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Tracker miners, indexing services for Tracker
+          search engine and metadata storage system.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.tracker-miners.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.tracker-miners ];
+
+    services.dbus.packages = [ pkgs.gnome3.tracker-miners ];
+
+    systemd.packages = [ pkgs.gnome3.tracker-miners ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/pipewire.nix b/nixos/modules/services/desktops/pipewire.nix
new file mode 100644
index 000000000000..263a06156f84
--- /dev/null
+++ b/nixos/modules/services/desktops/pipewire.nix
@@ -0,0 +1,23 @@
+# pipewire service.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.pipewire = {
+      enable = mkEnableOption "pipewire service";
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.pipewire.enable {
+    environment.systemPackages = [ pkgs.pipewire ];
+
+    systemd.packages = [ pkgs.pipewire ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ jtojnar ];
+}
diff --git a/nixos/modules/services/desktops/telepathy.nix b/nixos/modules/services/desktops/telepathy.nix
index 2554f3a1666f..f5401c180984 100644
--- a/nixos/modules/services/desktops/telepathy.nix
+++ b/nixos/modules/services/desktops/telepathy.nix
@@ -30,9 +30,9 @@ with lib;
 
   config = mkIf config.services.telepathy.enable {
 
-    environment.systemPackages = [ pkgs.telepathy_mission_control ];
+    environment.systemPackages = [ pkgs.telepathy-mission-control ];
 
-    services.dbus.packages = [ pkgs.telepathy_mission_control ];
+    services.dbus.packages = [ pkgs.telepathy-mission-control ];
 
   };
 
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 2c5a0c4849ef..ba7ec967919e 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -7,7 +7,7 @@ let
   cfg = config.services.emacs;
 
   editorScript = pkgs.writeScriptBin "emacseditor" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     if [ -z "$1" ]; then
       exec ${cfg.package}/bin/emacsclient --create-frame --alternate-editor ${cfg.package}/bin/emacs
     else
@@ -15,6 +15,25 @@ let
     fi
   '';
 
+desktopApplicationFile = pkgs.writeTextFile {
+  name = "emacsclient.desktop";
+  destination = "/share/applications/emacsclient.desktop";
+  text = ''
+[Desktop Entry]
+Name=Emacsclient
+GenericName=Text Editor
+Comment=Edit text
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+Exec=emacseditor %F
+Icon=emacs
+Type=Application
+Terminal=false
+Categories=Development;TextEditor;
+StartupWMClass=Emacs
+Keywords=Text;Editor;
+'';
+};
+
 in {
 
   options.services.emacs = {
@@ -74,7 +93,7 @@ in {
       };
     } // optionalAttrs cfg.enable { wantedBy = [ "default.target" ]; };
 
-    environment.systemPackages = [ cfg.package editorScript ];
+    environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ];
 
     environment.variables = {
       # This is required so that GTK applications launched from Emacs
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 1dc8ce93a0e5..3f6bf9de8931 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.factorio;
   factorio = pkgs.factorio-headless;
   name = "Factorio";
-  stateDir = "/var/lib/factorio";
+  stateDir = cfg.stateDir;
   mkSavePath = name: "${stateDir}/saves/${name}.zip";
   configFile = pkgs.writeText "factorio.conf" ''
     use-system-read-write-data-directories=true
@@ -25,7 +25,7 @@ let
     password = cfg.password;
     token = cfg.token;
     game_password = cfg.game-password;
-    require_user_verification = true;
+    require_user_verification = cfg.requireUserVerification;
     max_upload_in_kilobytes_per_second = 0;
     minimum_latency_in_ticks = 0;
     ignore_player_limit_for_returning_players = false;
@@ -80,6 +80,15 @@ in
           customizations.
         '';
       };
+      stateDir = mkOption {
+        type = types.path;
+        default = "/var/lib/factorio";
+        description = ''
+          The server's data directory.
+
+          The configuration and map will be stored here.
+        '';
+      };
       mods = mkOption {
         type = types.listOf types.package;
         default = [];
@@ -148,6 +157,13 @@ in
           Game password.
         '';
       };
+      requireUserVerification = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          When set to true, the server will only allow clients that have a valid factorio.com account.
+        '';
+      };
       autosave-interval = mkOption {
         type = types.nullOr types.int;
         default = null;
diff --git a/nixos/modules/services/games/ghost-one.nix b/nixos/modules/services/games/ghost-one.nix
deleted file mode 100644
index 71ff6bb2f3f0..000000000000
--- a/nixos/modules/services/games/ghost-one.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, lib, pkgs, ... }:
-with lib;
-let
-
-  cfg = config.services.ghostOne;
-  ghostUser = "ghostone";
-  stateDir = "/var/lib/ghost-one";
-
-in
-{
-
-  ###### interface
-
-  options = {
-    services.ghostOne = {
-
-      enable = mkOption {
-        default = false;
-        description = "Enable Ghost-One Warcraft3 game hosting server.";
-      };
-
-      language = mkOption {
-        default = "English";
-        type = types.enum [ "English" "Spanish" "Russian" "Serbian" "Turkish" ];
-        description = "The language of bot messages: English, Spanish, Russian, Serbian or Turkish.";
-      };
-
-      war3path = mkOption {
-        default = "";
-        description = ''
-          The path to your local Warcraft III directory, which must contain war3.exe, storm.dll, and game.dll.
-        '';
-      };
-
-      mappath = mkOption {
-        default = "";
-        description = ''
-          The path to the directory where you keep your map files. GHost One doesn't require
-          map files but if it has access to them it can send them to players and automatically
-          calculate most map config values. GHost One will search [bot_mappath + map_localpath]
-          for the map file (map_localpath is set in each map's config file).
-        '';
-      };
-
-      config = mkOption {
-        default = "";
-        description = "Extra configuration options.";
-      };
-
-    };
-  };
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    users.extraUsers = singleton
-      { name = ghostUser;
-        uid = config.ids.uids.ghostone;
-        description = "Ghost One game server user";
-        home = stateDir;
-      };
-
-    users.extraGroups = singleton
-      { name = ghostUser;
-        gid = config.ids.gids.ghostone;
-      };
-
-    services.ghostOne.config = ''
-#      bot_log = /dev/stderr
-      bot_language = ${pkgs.ghostOne}/share/ghost-one/languages/${cfg.language}.cfg
-      bot_war3path = ${cfg.war3path}
-
-      bot_mapcfgpath = mapcfgs
-      bot_savegamepath = savegames
-      bot_mappath = ${cfg.mappath}
-      bot_replaypath = replays
-    '';
-
-    systemd.services."ghost-one" = {
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        mkdir -p ${stateDir}
-        cd ${stateDir}
-        chown ${ghostUser}:${ghostUser} .
-
-        mkdir -p mapcfgs
-        chown ${ghostUser}:${ghostUser} mapcfgs
-
-        mkdir -p replays
-        chown ${ghostUser}:${ghostUser} replays
-
-        mkdir -p savegames
-        chown ${ghostUser}:${ghostUser} savegames
-
-        ln -sf ${pkgs.writeText "ghost.cfg" cfg.config} ghost.cfg
-        ln -sf ${pkgs.ghostOne}/share/ghost-one/ip-to-country.csv
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${ghostUser} \
-          -c "LANG=C ${pkgs.ghostOne}/bin/ghost++"
-      '';
-    };
-
-  };
-
-}
diff --git a/nixos/modules/services/hardware/acpid.nix b/nixos/modules/services/hardware/acpid.nix
index bb17c8859d84..0f05876aee32 100644
--- a/nixos/modules/services/hardware/acpid.nix
+++ b/nixos/modules/services/hardware/acpid.nix
@@ -31,7 +31,7 @@ let
           ''
             fn=$out/${name}
             echo "event=${handler.event}" > $fn
-            echo "action=${pkgs.writeScript "${name}.sh" (concatStringsSep "\n" [ "#! ${pkgs.bash}/bin/sh" handler.action ])}" >> $fn
+            echo "action=${pkgs.writeShellScriptBin "${name}.sh" handler.action }/bin/${name}.sh '%e'" >> $fn
           '';
         in concatStringsSep "\n" (mapAttrsToList f (canonicalHandlers // config.services.acpid.handlers))
       }
@@ -53,6 +53,12 @@ in
         description = "Whether to enable the ACPI daemon.";
       };
 
+      logEvents = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Log all event activity.";
+      };
+
       handlers = mkOption {
         type = types.attrsOf (types.submodule {
           options = {
@@ -69,11 +75,33 @@ in
           };
         });
 
-        description = "Event handlers.";
-        default = {};
-        example = { mute = { event = "button/mute.*"; action = "amixer set Master toggle"; }; };
-
+        description = ''
+          Event handlers.
 
+          <note><para>
+            Handler can be a single command.
+          </para></note>
+        '';
+        default = {};
+        example = {
+          ac-power = {
+            event = "ac_adapter/*";
+            action = ''
+              vals=($1)  # space separated string to array of multiple values
+              case ''${vals[3]} in
+                  00000000)
+                      echo unplugged >> /tmp/acpi.log
+                      ;;
+                  00000001)
+                      echo plugged in >> /tmp/acpi.log
+                      ;;
+                  *)
+                      echo unknown >> /tmp/acpi.log
+                      ;;
+              esac
+            '';
+          };
+        };
       };
 
       powerEventCommands = mkOption {
@@ -120,7 +148,7 @@ in
         ConditionPathExists = [ "/proc/acpi" ];
       };
 
-      script = "acpid --confdir ${acpiConfDir}";
+      script = "acpid ${optionalString config.services.acpid.logEvents "--logevents"} --confdir ${acpiConfDir}";
     };
 
   };
diff --git a/nixos/modules/services/hardware/amd-hybrid-graphics.nix b/nixos/modules/services/hardware/amd-hybrid-graphics.nix
deleted file mode 100644
index b0f9ff56d1b2..000000000000
--- a/nixos/modules/services/hardware/amd-hybrid-graphics.nix
+++ /dev/null
@@ -1,46 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-{
-
-  ###### interface
-
-  options = {
-
-    hardware.amdHybridGraphics.disable = lib.mkOption {
-      default = false;
-      type = lib.types.bool;
-      description = ''
-        Completely disable the AMD graphics card and use the
-        integrated graphics processor instead.
-      '';
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = lib.mkIf config.hardware.amdHybridGraphics.disable {
-    systemd.services."amd-hybrid-graphics" = {
-      path = [ pkgs.bash ];
-      description = "Disable AMD Card";
-      after = [ "sys-kernel-debug.mount" ];
-      before = [ "systemd-vconsole-setup.service" "display-manager.service" ];
-      requires = [ "sys-kernel-debug.mount" "vgaswitcheroo.path" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-        ExecStart = "${pkgs.bash}/bin/sh -c 'echo -e \"IGD\\nOFF\" > /sys/kernel/debug/vgaswitcheroo/switch'";
-        ExecStop = "${pkgs.bash}/bin/sh -c 'echo ON >/sys/kernel/debug/vgaswitcheroo/switch'";
-      };
-    };
-    systemd.paths."vgaswitcheroo" = {
-      pathConfig = {
-        PathExists = "/sys/kernel/debug/vgaswitcheroo/switch";
-        Unit = "amd-hybrid-graphics.service";
-      };
-      wantedBy = ["multi-user.target"];
-    };
-  };
-
-}
diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix
index 4a8cd86b0b11..d7ca8a431794 100644
--- a/nixos/modules/services/hardware/bluetooth.nix
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -3,8 +3,8 @@
 with lib;
 
 let
-  bluez-bluetooth = pkgs.bluez;
   cfg = config.hardware.bluetooth;
+  bluez-bluetooth = cfg.package;
 
 in {
 
@@ -21,6 +21,16 @@ in {
         description = "Whether to power up the default Bluetooth controller on boot.";
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.bluez;
+        defaultText = "pkgs.bluez";
+        example = "pkgs.bluez.override { enableMidi = true; }";
+        description = ''
+          Which BlueZ package to use.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix
new file mode 100644
index 000000000000..d8abde2a600a
--- /dev/null
+++ b/nixos/modules/services/hardware/fwupd.nix
@@ -0,0 +1,90 @@
+# fwupd daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.fwupd;
+  originalEtc =
+    let
+      mkEtcFile = n: nameValuePair n { source = "${pkgs.fwupd}/etc/${n}"; };
+    in listToAttrs (map mkEtcFile pkgs.fwupd.filesInstalledToEtc);
+  extraTrustedKeys =
+    let
+      mkName = p: "pki/fwupd/${baseNameOf (toString p)}";
+      mkEtcFile = p: nameValuePair (mkName p) { source = p; };
+    in listToAttrs (map mkEtcFile cfg.extraTrustedKeys);
+in {
+
+  ###### interface
+  options = {
+    services.fwupd = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable fwupd, a DBus service that allows
+          applications to update firmware.
+        '';
+      };
+
+      blacklistDevices = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = [ "2082b5e0-7a64-478a-b1b2-e3404fab6dad" ];
+        description = ''
+          Allow blacklisting specific devices by their GUID
+        '';
+      };
+
+      blacklistPlugins = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = [ "udev" ];
+        description = ''
+          Allow blacklisting specific plugins
+        '';
+      };
+
+      extraTrustedKeys = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        example = literalExample "[ /etc/nixos/fwupd/myfirmware.pem ]";
+        description = ''
+          Installing a public key allows firmware signed with a matching private key to be recognized as trusted, which may require less authentication to install than for untrusted files. By default trusted firmware can be upgraded (but not downgraded) without the user or administrator password. Only very few keys are installed by default.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.fwupd ];
+
+    environment.etc = {
+      "fwupd/daemon.conf" = {
+        source = pkgs.writeText "daemon.conf" ''
+          [fwupd]
+          BlacklistDevices=${lib.concatStringsSep ";" cfg.blacklistDevices}
+          BlacklistPlugins=${lib.concatStringsSep ";" cfg.blacklistPlugins}
+        '';
+      };
+    } // originalEtc // extraTrustedKeys;
+
+    services.dbus.packages = [ pkgs.fwupd ];
+
+    services.udev.packages = [ pkgs.fwupd ];
+
+    systemd.packages = [ pkgs.fwupd ];
+
+    systemd.tmpfiles.rules = [
+      "d /var/lib/fwupd 0755 root root -"
+    ];
+  };
+
+  meta = {
+    maintainers = pkgs.fwupd.maintainers;
+  };
+}
diff --git a/nixos/modules/services/hardware/nvidia-optimus.nix b/nixos/modules/services/hardware/nvidia-optimus.nix
index 9fe4021c4247..eb1713baa140 100644
--- a/nixos/modules/services/hardware/nvidia-optimus.nix
+++ b/nixos/modules/services/hardware/nvidia-optimus.nix
@@ -23,7 +23,7 @@ let kernel = config.boot.kernelPackages; in
   ###### implementation
 
   config = lib.mkIf config.hardware.nvidiaOptimus.disable {
-    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb"];
+    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb" "nvidia-drm"];
     boot.kernelModules = [ "bbswitch" ];
     boot.extraModulePackages = [ kernel.bbswitch ];
 
diff --git a/nixos/modules/services/hardware/thinkfan.nix b/nixos/modules/services/hardware/thinkfan.nix
index 018e82e58a3d..5a898631e090 100644
--- a/nixos/modules/services/hardware/thinkfan.nix
+++ b/nixos/modules/services/hardware/thinkfan.nix
@@ -55,7 +55,7 @@ in {
       enable = mkOption {
         default = false;
         description = ''
-          Whether to enable thinkfan, fan controller for ibm/lenovo thinkpads.
+          Whether to enable thinkfan, fan controller for IBM/Lenovo ThinkPads.
         '';
       };
 
diff --git a/nixos/modules/services/hardware/trezord.nix b/nixos/modules/services/hardware/trezord.nix
index 38d0a3a1d752..fa0496114684 100644
--- a/nixos/modules/services/hardware/trezord.nix
+++ b/nixos/modules/services/hardware/trezord.nix
@@ -38,7 +38,7 @@ in {
       path = [];
       serviceConfig = {
         Type = "simple";
-        ExecStart = "${pkgs.trezord}/bin/trezord -f";
+        ExecStart = "${pkgs.trezord}/bin/trezord-go";
         User = "trezord";
       };
     };
diff --git a/nixos/modules/services/hardware/u2f.nix b/nixos/modules/services/hardware/u2f.nix
new file mode 100644
index 000000000000..bb4b2f05f890
--- /dev/null
+++ b/nixos/modules/services/hardware/u2f.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.u2f;
+in {
+  options = {
+    hardware.u2f = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable U2F hardware support.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.libu2f-host ];
+  };
+}
+
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 9f42f9e59ad5..7bfc3bb64872 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -146,7 +146,7 @@ let
 
       echo "Generating hwdb database..."
       # hwdb --update doesn't return error code even on errors!
-      res="$(${udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)"
+      res="$(${pkgs.buildPackages.udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)"
       echo "$res"
       [ -z "$(echo "$res" | egrep '^Error')" ]
       mv etc/udev/hwdb.bin $out
diff --git a/nixos/modules/services/hardware/usbmuxd.nix b/nixos/modules/services/hardware/usbmuxd.nix
new file mode 100644
index 000000000000..7ebd49fa01c2
--- /dev/null
+++ b/nixos/modules/services/hardware/usbmuxd.nix
@@ -0,0 +1,74 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  defaultUserGroup = "usbmux";
+  apple = "05ac";
+
+  cfg = config.services.usbmuxd;
+
+in
+
+{
+  options.services.usbmuxd = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable the usbmuxd ("USB multiplexing daemon") service. This daemon is
+        in charge of multiplexing connections over USB to an iOS device. This is
+        needed for transferring data from and to iOS devices (see ifuse). Also
+        this may enable plug-n-play tethering for iPhones.
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUserGroup;
+      description = ''
+        The user usbmuxd should use to run after startup.
+      '';
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = defaultUserGroup;
+      description = ''
+        The group usbmuxd should use to run after startup.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = optional (cfg.user == defaultUserGroup) {
+      name = cfg.user;
+      description = "usbmuxd user";
+      group = cfg.group;
+    };
+
+    users.extraGroups = optional (cfg.group == defaultUserGroup) {
+      name = cfg.group;
+    };
+
+    # Give usbmuxd permission for Apple devices
+    services.udev.extraRules = ''
+      SUBSYSTEM=="usb", ATTR{idVendor}=="${apple}", GROUP="${cfg.group}"
+    '';
+
+    systemd.services.usbmuxd = {
+      description = "usbmuxd";
+      wantedBy = [ "multi-user.target" ];
+      unitConfig.Documentation = "man:usbmuxd(8)";
+      serviceConfig = {
+        # Trigger the udev rule manually. This doesn't require replugging the
+        # device when first enabling the option to get it to work
+        ExecStartPre = "${pkgs.libudev}/bin/udevadm trigger -s usb -a idVendor=${apple}";
+        ExecStart = "${pkgs.usbmuxd}/bin/usbmuxd -U ${cfg.user} -f";
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix
index a0dc0d6d089d..95f31829882f 100644
--- a/nixos/modules/services/logging/graylog.nix
+++ b/nixos/modules/services/logging/graylog.nix
@@ -141,7 +141,7 @@ in
         JAVA_HOME = jre;
         GRAYLOG_CONF = "${confFile}";
       };
-      path = [ pkgs.openjdk8 pkgs.which pkgs.procps ];
+      path = [ pkgs.jre_headless pkgs.which pkgs.procps ];
       preStart = ''
         mkdir -p /var/lib/graylog -m 755
 
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 2a8ac414720b..a4cab0c94cdc 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -8,7 +8,7 @@ let
   defaultRules = pkgs.runCommand "logcheck-default-rules" {} ''
                    cp -prd ${pkgs.logcheck}/etc/logcheck $out
                    chmod u+w $out
-                   rm $out/logcheck.*
+                   rm -r $out/logcheck.*
                  '';
 
   rulesDir = pkgs.symlinkJoin
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index b4abd2cd7e5e..28d89a7463ab 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -103,7 +103,7 @@ in
 
       listenAddress = mkOption {
         type = types.str;
-        default = "0.0.0.0";
+        default = "127.0.0.1";
         description = "Address on which to start webserver.";
       };
 
diff --git a/nixos/modules/services/mail/clamsmtp.nix b/nixos/modules/services/mail/clamsmtp.nix
new file mode 100644
index 000000000000..8f4f39aa7288
--- /dev/null
+++ b/nixos/modules/services/mail/clamsmtp.nix
@@ -0,0 +1,179 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.clamsmtp;
+  clamdSocket = "/run/clamav/clamd.ctl"; # See services/security/clamav.nix
+in
+{
+  ##### interface
+  options = {
+    services.clamsmtp = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable clamsmtp.";
+      };
+
+      instances = mkOption {
+        description = "Instances of clamsmtp to run.";
+        type = types.listOf (types.submodule { options = {
+          action = mkOption {
+            type = types.enum [ "bounce" "drop" "pass" ];
+            default = "drop";
+            description =
+              ''
+                Action to take when a virus is detected.
+
+                Note that viruses often spoof sender addresses, so bouncing is
+                in most cases not a good idea.
+              '';
+          };
+
+          header = mkOption {
+            type = types.str;
+            default = "";
+            example = "X-Virus-Scanned: ClamAV using ClamSMTP";
+            description =
+              ''
+                A header to add to scanned messages. See clamsmtpd.conf(5) for
+                more details. Empty means no header.
+              '';
+          };
+
+          keepAlives = mkOption {
+            type = types.int;
+            default = 0;
+            description =
+              ''
+                Number of seconds to wait between each NOOP sent to the sending
+                server. 0 to disable.
+
+                This is meant for slow servers where the sending MTA times out
+                waiting for clamd to scan the file.
+              '';
+          };
+
+          listen = mkOption {
+            type = types.str;
+            example = "127.0.0.1:10025";
+            description =
+              ''
+                Address to wait for incoming SMTP connections on. See
+                clamsmtpd.conf(5) for more details.
+              '';
+          };
+
+          quarantine = mkOption {
+            type = types.bool;
+            default = false;
+            description =
+              ''
+                Whether to quarantine files that contain viruses by leaving them
+                in the temporary directory.
+              '';
+          };
+
+          maxConnections = mkOption {
+            type = types.int;
+            default = 64;
+            description = "Maximum number of connections to accept at once.";
+          };
+
+          outAddress = mkOption {
+            type = types.str;
+            description =
+              ''
+                Address of the SMTP server to send email to once it has been
+                scanned.
+              '';
+          };
+
+          tempDirectory = mkOption {
+            type = types.str;
+            default = "/tmp";
+            description =
+              ''
+                Temporary directory that needs to be accessible to both clamd
+                and clamsmtpd.
+              '';
+          };
+
+          timeout = mkOption {
+            type = types.int;
+            default = 180;
+            description = "Time-out for network connections.";
+          };
+
+          transparentProxy = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Enable clamsmtp's transparent proxy support.";
+          };
+
+          virusAction = mkOption {
+            type = with types; nullOr path;
+            default = null;
+            description =
+              ''
+                Command to run when a virus is found. Please see VIRUS ACTION in
+                clamsmtpd(8) for a discussion of this option and its safe use.
+              '';
+          };
+
+          xClient = mkOption {
+            type = types.bool;
+            default = false;
+            description =
+              ''
+                Send the XCLIENT command to the receiving server, for forwarding
+                client addresses and connection information if the receiving
+                server supports this feature.
+              '';
+          };
+        };});
+      };
+    };
+  };
+
+  ##### implementation
+  config = let
+    configfile = conf: pkgs.writeText "clamsmtpd.conf"
+      ''
+        Action: ${conf.action}
+        ClamAddress: ${clamdSocket}
+        Header: ${conf.header}
+        KeepAlives: ${toString conf.keepAlives}
+        Listen: ${conf.listen}
+        Quarantine: ${if conf.quarantine then "on" else "off"}
+        MaxConnections: ${toString conf.maxConnections}
+        OutAddress: ${conf.outAddress}
+        TempDirectory: ${conf.tempDirectory}
+        TimeOut: ${toString conf.timeout}
+        TransparentProxy: ${if conf.transparentProxy then "on" else "off"}
+        User: clamav
+        ${optionalString (conf.virusAction != null) "VirusAction: ${conf.virusAction}"}
+        XClient: ${if conf.xClient then "on" else "off"}
+      '';
+  in
+    mkIf cfg.enable {
+      assertions = [
+        { assertion = config.services.clamav.daemon.enable;
+          message = "clamsmtp requires clamav to be enabled";
+        }
+      ];
+
+      systemd.services = listToAttrs (imap1 (i: conf:
+        nameValuePair "clamsmtp-${toString i}" {
+          description = "ClamSMTP instance ${toString i}";
+          wantedBy = [ "multi-user.target" ];
+          script = "exec ${pkgs.clamsmtp}/bin/clamsmtpd -f ${configfile conf}";
+          after = [ "clamav-daemon.service" ];
+          requires = [ "clamav-daemon.service" ];
+          serviceConfig.Type = "forking";
+          serviceConfig.PrivateTmp = "yes";
+          unitConfig.JoinsNamespaceOf = "clamav-daemon.service";
+        }
+      ) cfg.instances);
+    };
+}
diff --git a/nixos/modules/services/mail/dkimproxy-out.nix b/nixos/modules/services/mail/dkimproxy-out.nix
new file mode 100644
index 000000000000..894b88e25c1b
--- /dev/null
+++ b/nixos/modules/services/mail/dkimproxy-out.nix
@@ -0,0 +1,118 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.dkimproxy-out;
+  keydir = "/var/lib/dkimproxy-out";
+  privkey = "${keydir}/private.key";
+  pubkey = "${keydir}/public.key";
+in
+{
+  ##### interface
+  options = {
+    services.dkimproxy-out = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            Whether to enable dkimproxy_out.
+
+            Note that a key will be auto-generated, and can be found in
+            ${keydir}.
+          '';
+      };
+
+      listen = mkOption {
+        type = types.str;
+        example = "127.0.0.1:10027";
+        description = "Address:port DKIMproxy should listen on.";
+      };
+
+      relay = mkOption {
+        type = types.str;
+        example = "127.0.0.1:10028";
+        description = "Address:port DKIMproxy should forward mail to.";
+      };
+
+      domains = mkOption {
+        type = with types; listOf str;
+        example = [ "example.org" "example.com" ];
+        description = "List of domains DKIMproxy can sign for.";
+      };
+
+      selector = mkOption {
+        type = types.str;
+        example = "selector1";
+        description =
+          ''
+            The selector to use for DKIM key identification.
+
+            For example, if 'selector1' is used here, then for each domain
+            'example.org' given in `domain`, 'selector1._domainkey.example.org'
+            should contain the TXT record indicating the public key is the one
+            in ${pubkey}: "v=DKIM1; t=s; p=[THE PUBLIC KEY]".
+          '';
+      };
+
+      keySize = mkOption {
+        type = types.int;
+        default = 2048;
+        description =
+          ''
+            Size of the RSA key to use to sign outgoing emails. Note that the
+            maximum mandatorily verified as per RFC6376 is 2048.
+          '';
+      };
+
+      # TODO: allow signature for other schemes than dkim(c=relaxed/relaxed)?
+      # This being the scheme used by gmail, maybe nothing more is needed for
+      # reasonable use.
+    };
+  };
+
+  ##### implementation
+  config = let
+    configfile = pkgs.writeText "dkimproxy_out.conf"
+      ''
+        listen ${cfg.listen}
+        relay ${cfg.relay}
+
+        domain ${concatStringsSep "," cfg.domains}
+        selector ${cfg.selector}
+
+        signature dkim(c=relaxed/relaxed)
+
+        keyfile ${privkey}
+      '';
+  in
+    mkIf cfg.enable {
+      users.groups.dkimproxy-out = {};
+      users.users.dkimproxy-out = {
+        description = "DKIMproxy_out daemon";
+        group = "dkimproxy-out";
+        isSystemUser = true;
+      };
+
+      systemd.services.dkimproxy-out = {
+        description = "DKIMproxy_out";
+        wantedBy = [ "multi-user.target" ];
+        preStart = ''
+          if [ ! -d "${keydir}" ]; then
+            mkdir -p "${keydir}"
+            chmod 0700 "${keydir}"
+            ${pkgs.openssl}/bin/openssl genrsa -out "${privkey}" ${toString cfg.keySize}
+            ${pkgs.openssl}/bin/openssl rsa -in "${privkey}" -pubout -out "${pubkey}"
+            chown -R dkimproxy-out:dkimproxy-out "${keydir}"
+          fi
+        '';
+        script = ''
+          exec ${pkgs.dkimproxy}/bin/dkimproxy.out --conf_file=${configfile}
+        '';
+        serviceConfig = {
+          User = "dkimproxy-out";
+          PermissionsStartOnly = true;
+        };
+      };
+    };
+}
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 18101a312254..543e732127a5 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -30,6 +30,7 @@ let
 
     ''
       default_internal_user = ${cfg.user}
+      default_internal_group = ${cfg.group}
       ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"}
       ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"}
 
@@ -104,7 +105,7 @@ let
   };
 
   mailboxConfig = mailbox: ''
-    mailbox ${mailbox.name} {
+    mailbox "${mailbox.name}" {
       auto = ${toString mailbox.auto}
   '' + optionalString (mailbox.specialUse != null) ''
       special_use = \${toString mailbox.specialUse}
@@ -113,7 +114,7 @@ let
   mailboxes = { lib, pkgs, ... }: {
     options = {
       name = mkOption {
-        type = types.str;
+        type = types.strMatching ''[^"]+'';
         example = "Spam";
         description = "The name of the mailbox.";
       };
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index 867c0ea6761c..5ab331ac067f 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -15,20 +15,18 @@ let
   haveVirtual = cfg.virtual != "";
 
   clientAccess =
-    if (cfg.dnsBlacklistOverrides != "")
-    then [ "check_client_access hash:/etc/postfix/client_access" ]
-    else [];
+    optional (cfg.dnsBlacklistOverrides != "")
+      "check_client_access hash:/etc/postfix/client_access";
 
   dnsBl =
-    if (cfg.dnsBlacklists != [])
-    then [ (concatStringsSep ", " (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists)) ]
-    else [];
+    optionals (cfg.dnsBlacklists != [])
+      (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists);
 
   clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl);
 
   mainCf = let
     escape = replaceStrings ["$"] ["$$"];
-    mkList = items: "\n  " + concatStringsSep "\n  " items;
+    mkList = items: "\n  " + concatStringsSep ",\n  " items;
     mkVal = value:
       if isList value then mkList value
         else " " + (if value == true then "yes"
@@ -36,72 +34,9 @@ let
         else toString value);
     mkEntry = name: value: "${escape name} =${mkVal value}";
   in
-    concatStringsSep "\n" (mapAttrsToList mkEntry (recursiveUpdate defaultConf cfg.config))
+    concatStringsSep "\n" (mapAttrsToList mkEntry cfg.config)
       + "\n" + cfg.extraConfig;
 
-  defaultConf = {
-    compatibility_level  = "9999";
-    mail_owner           = user;
-    default_privs        = "nobody";
-
-    # NixOS specific locations
-    data_directory       = "/var/lib/postfix/data";
-    queue_directory      = "/var/lib/postfix/queue";
-
-    # Default location of everything in package
-    meta_directory       = "${pkgs.postfix}/etc/postfix";
-    command_directory    = "${pkgs.postfix}/bin";
-    sample_directory     = "/etc/postfix";
-    newaliases_path      = "${pkgs.postfix}/bin/newaliases";
-    mailq_path           = "${pkgs.postfix}/bin/mailq";
-    readme_directory     = false;
-    sendmail_path        = "${pkgs.postfix}/bin/sendmail";
-    daemon_directory     = "${pkgs.postfix}/libexec/postfix";
-    manpage_directory    = "${pkgs.postfix}/share/man";
-    html_directory       = "${pkgs.postfix}/share/postfix/doc/html";
-    shlib_directory      = false;
-    relayhost            = if cfg.relayHost == "" then "" else
-                             if cfg.lookupMX
-                             then "${cfg.relayHost}:${toString cfg.relayPort}"
-                             else "[${cfg.relayHost}]:${toString cfg.relayPort}";
-
-    mail_spool_directory = "/var/spool/mail/";
-    setgid_group         = setgidGroup;
-  }
-  // optionalAttrs config.networking.enableIPv6 { inet_protocols = "all"; }
-  // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; }
-  // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; }
-  // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; }
-  // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; }
-  // optionalAttrs (cfg.origin != "") { myorigin =  cfg.origin; }
-  // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; }
-  // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; }
-  // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; }
-  // optionalAttrs haveAliases { alias_maps = "${cfg.aliasMapType}:/etc/postfix/aliases"; }
-  // optionalAttrs haveTransport { transport_maps = "hash:/etc/postfix/transport"; }
-  // optionalAttrs haveVirtual { virtual_alias_maps = "${cfg.virtualMapType}:/etc/postfix/virtual"; }
-  // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
-  // optionalAttrs cfg.useSrs {
-    sender_canonical_maps = "tcp:127.0.0.1:10001";
-    sender_canonical_classes = "envelope_sender";
-    recipient_canonical_maps = "tcp:127.0.0.1:10002";
-    recipient_canonical_classes= "envelope_recipient";
-  }
-  // optionalAttrs cfg.enableHeaderChecks { header_checks = "regexp:/etc/postfix/header_checks"; }
-  // optionalAttrs (cfg.sslCert != "") {
-    smtp_tls_CAfile = cfg.sslCACert;
-    smtp_tls_cert_file = cfg.sslCert;
-    smtp_tls_key_file = cfg.sslKey;
-
-    smtp_use_tls = true;
-
-    smtpd_tls_CAfile = cfg.sslCACert;
-    smtpd_tls_cert_file = cfg.sslCert;
-    smtpd_tls_key_file = cfg.sslKey;
-
-    smtpd_use_tls = true;
-  };
-
   masterCfOptions = { options, config, name, ... }: {
     options = {
       name = mkOption {
@@ -479,7 +414,10 @@ in
       postmasterAlias = mkOption {
         type = types.str;
         default = "root";
-        description = "Who should receive postmaster e-mail.";
+        description = "
+          Who should receive postmaster e-mail. Multiple values can be added by
+          separating values with comma.
+        ";
       };
 
       rootAlias = mkOption {
@@ -487,6 +425,7 @@ in
         default = "";
         description = "
           Who should receive root e-mail. Blank for no redirection.
+          Multiple values can be added by separating values with comma.
         ";
       };
 
@@ -507,7 +446,6 @@ in
 
       config = mkOption {
         type = with types; attrsOf (either bool (either str (listOf str)));
-        default = defaultConf;
         description = ''
           The main.cf configuration file as key value set.
         '';
@@ -749,6 +687,67 @@ in
           '';
         };
 
+      services.postfix.config = (mapAttrs (_: v: mkDefault v) {
+        compatibility_level  = "9999";
+        mail_owner           = cfg.user;
+        default_privs        = "nobody";
+
+        # NixOS specific locations
+        data_directory       = "/var/lib/postfix/data";
+        queue_directory      = "/var/lib/postfix/queue";
+
+        # Default location of everything in package
+        meta_directory       = "${pkgs.postfix}/etc/postfix";
+        command_directory    = "${pkgs.postfix}/bin";
+        sample_directory     = "/etc/postfix";
+        newaliases_path      = "${pkgs.postfix}/bin/newaliases";
+        mailq_path           = "${pkgs.postfix}/bin/mailq";
+        readme_directory     = false;
+        sendmail_path        = "${pkgs.postfix}/bin/sendmail";
+        daemon_directory     = "${pkgs.postfix}/libexec/postfix";
+        manpage_directory    = "${pkgs.postfix}/share/man";
+        html_directory       = "${pkgs.postfix}/share/postfix/doc/html";
+        shlib_directory      = false;
+        mail_spool_directory = "/var/spool/mail/";
+        setgid_group         = cfg.setgidGroup;
+      })
+      // optionalAttrs (cfg.relayHost != "") { relayhost = if cfg.lookupMX
+                                                           then "${cfg.relayHost}:${toString cfg.relayPort}"
+                                                           else "[${cfg.relayHost}]:${toString cfg.relayPort}"; }
+      // optionalAttrs config.networking.enableIPv6 { inet_protocols = mkDefault "all"; }
+      // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; }
+      // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; }
+      // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; }
+      // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; }
+      // optionalAttrs (cfg.origin != "") { myorigin =  cfg.origin; }
+      // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; }
+      // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; }
+      // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; }
+      // optionalAttrs haveAliases { alias_maps = [ "${cfg.aliasMapType}:/etc/postfix/aliases" ]; }
+      // optionalAttrs haveTransport { transport_maps = [ "hash:/etc/postfix/transport" ]; }
+      // optionalAttrs haveVirtual { virtual_alias_maps = [ "${cfg.virtualMapType}:/etc/postfix/virtual" ]; }
+      // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
+      // optionalAttrs cfg.useSrs {
+        sender_canonical_maps = [ "tcp:127.0.0.1:10001" ];
+        sender_canonical_classes = [ "envelope_sender" ];
+        recipient_canonical_maps = [ "tcp:127.0.0.1:10002" ];
+        recipient_canonical_classes = [ "envelope_recipient" ];
+      }
+      // optionalAttrs cfg.enableHeaderChecks { header_checks = [ "regexp:/etc/postfix/header_checks" ]; }
+      // optionalAttrs (cfg.sslCert != "") {
+        smtp_tls_CAfile = cfg.sslCACert;
+        smtp_tls_cert_file = cfg.sslCert;
+        smtp_tls_key_file = cfg.sslKey;
+
+        smtp_use_tls = true;
+
+        smtpd_tls_CAfile = cfg.sslCACert;
+        smtpd_tls_cert_file = cfg.sslCert;
+        smtpd_tls_key_file = cfg.sslKey;
+
+        smtpd_use_tls = true;
+      };
+
       services.postfix.masterConfig = {
         smtp_inet = {
           name = "smtp";
diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index 6d403e448e04..09fb587e74b5 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -1,14 +1,152 @@
-{ config, lib, pkgs, ... }:
+{ config, options, pkgs, lib, ... }:
 
 with lib;
 
 let
 
   cfg = config.services.rspamd;
+  opts = options.services.rspamd;
 
-  mkBindSockets = socks: concatStringsSep "\n" (map (each: "  bind_socket = \"${each}\"") socks);
+  bindSocketOpts = {options, config, ... }: {
+    options = {
+      socket = mkOption {
+        type = types.str;
+        example = "localhost:11333";
+        description = ''
+          Socket for this worker to listen on in a format acceptable by rspamd.
+        '';
+      };
+      mode = mkOption {
+        type = types.str;
+        default = "0644";
+        description = "Mode to set on unix socket";
+      };
+      owner = mkOption {
+        type = types.str;
+        default = "${cfg.user}";
+        description = "Owner to set on unix socket";
+      };
+      group = mkOption {
+        type = types.str;
+        default = "${cfg.group}";
+        description = "Group to set on unix socket";
+      };
+      rawEntry = mkOption {
+        type = types.str;
+        internal = true;
+      };
+    };
+    config.rawEntry = let
+      maybeOption = option:
+        optionalString options.${option}.isDefined " ${option}=${config.${option}}";
+    in
+      if (!(hasPrefix "/" config.socket)) then "${config.socket}"
+      else "${config.socket}${maybeOption "mode"}${maybeOption "owner"}${maybeOption "group"}";
+  };
 
-   rspamdConfFile = pkgs.writeText "rspamd.conf"
+  workerOpts = { name, ... }: {
+    options = {
+      enable = mkOption {
+        type = types.nullOr types.bool;
+        default = null;
+        description = "Whether to run the rspamd worker.";
+      };
+      name = mkOption {
+        type = types.nullOr types.str;
+        default = name;
+        description = "Name of the worker";
+      };
+      type = mkOption {
+        type = types.nullOr (types.enum [
+          "normal" "controller" "fuzzy_storage" "proxy" "lua"
+        ]);
+        description = "The type of this worker";
+      };
+      bindSockets = mkOption {
+        type = types.listOf (types.either types.str (types.submodule bindSocketOpts));
+        default = [];
+        description = ''
+          List of sockets to listen, in format acceptable by rspamd
+        '';
+        example = [{
+          socket = "/run/rspamd.sock";
+          mode = "0666";
+          owner = "rspamd";
+        } "*:11333"];
+        apply = value: map (each: if (isString each)
+          then if (isUnixSocket each)
+            then {socket = each; owner = cfg.user; group = cfg.group; mode = "0644"; rawEntry = "${each}";}
+            else {socket = each; rawEntry = "${each}";}
+          else each) value;
+      };
+      count = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Number of worker instances to run
+        '';
+      };
+      includes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of files to include in configuration
+        '';
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional entries to put verbatim into worker section of rspamd config file.";
+      };
+    };
+    config = mkIf (name == "normal" || name == "controller" || name == "fuzzy") {
+      type = mkDefault name;
+      includes = mkDefault [ "$CONFDIR/worker-${name}.inc" ];
+      bindSockets = mkDefault (if name == "normal"
+        then [{
+              socket = "/run/rspamd/rspamd.sock";
+              mode = "0660";
+              owner = cfg.user;
+              group = cfg.group;
+            }]
+        else if name == "controller"
+        then [ "localhost:11334" ]
+        else [] );
+    };
+  };
+
+  indexOf = default: start: list: e:
+    if list == []
+    then default
+    else if (head list) == e then start
+    else (indexOf default (start + (length (listenStreams (head list).socket))) (tail list) e);
+
+  systemdSocket = indexOf (abort "Socket not found") 0 allSockets;
+
+  isUnixSocket = socket: hasPrefix "/" (if (isString socket) then socket else socket.socket);
+  isPort = hasPrefix "*:";
+  isIPv4Socket = hasPrefix "*v4:";
+  isIPv6Socket = hasPrefix "*v6:";
+  isLocalHost = hasPrefix "localhost:";
+  listenStreams = socket:
+    if (isLocalHost socket) then
+      let port = (removePrefix "localhost:" socket);
+      in [ "127.0.0.1:${port}" ] ++ (if config.networking.enableIPv6 then ["[::1]:${port}"] else [])
+    else if (isIPv6Socket socket) then [removePrefix "*v6:" socket]
+    else if (isPort socket) then [removePrefix "*:" socket]
+    else if (isIPv4Socket socket) then
+      throw "error: IPv4 only socket not supported in rspamd with socket activation"
+    else if (length (splitString " " socket)) != 1 then
+      throw "error: string options not supported in rspamd with socket activation"
+    else [socket];
+
+  mkBindSockets = enabled: socks: concatStringsSep "\n  " (flatten (map (each:
+    if cfg.socketActivation && enabled != false then
+      let systemd = (systemdSocket each);
+      in (imap (idx: e: "bind_socket = \"systemd:${toString (systemd + idx - 1)}\";") (listenStreams each.socket))
+    else "bind_socket = \"${each.rawEntry}\";") socks));
+
+  rspamdConfFile = pkgs.writeText "rspamd.conf"
     ''
       .include "$CONFDIR/common.conf"
 
@@ -22,17 +160,33 @@ let
         .include "$CONFDIR/logging.inc"
       }
 
-      worker {
-      ${mkBindSockets cfg.bindSocket}
-        .include "$CONFDIR/worker-normal.inc"
-      }
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
+        worker ${optionalString (value.name != "normal" && value.name != "controller") "${value.name}"} {
+          type = "${value.type}";
+          ${optionalString (value.enable != null)
+            "enabled = ${if value.enable != false then "yes" else "no"};"}
+          ${mkBindSockets value.enable value.bindSockets}
+          ${optionalString (value.count != null) "count = ${toString value.count};"}
+          ${concatStringsSep "\n  " (map (each: ".include \"${each}\"") value.includes)}
+          ${value.extraConfig}
+        }
+      '') cfg.workers)}
 
-      worker {
-      ${mkBindSockets cfg.bindUISocket}
-        .include "$CONFDIR/worker-controller.inc"
-      }
+      ${cfg.extraConfig}
    '';
 
+  allMappedSockets = flatten (mapAttrsToList (name: value:
+    if value.enable != false
+    then imap (idx: each: {
+        name = "${name}";
+        index = idx;
+        value = each;
+      }) value.bindSockets
+    else []) cfg.workers);
+  allSockets = map (e: e.value) allMappedSockets;
+
+  allSocketNames = map (each: "rspamd-${each.name}-${toString each.index}.socket") allMappedSockets;
+
 in
 
 {
@@ -46,36 +200,52 @@ in
       enable = mkEnableOption "Whether to run the rspamd daemon.";
 
       debug = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to run the rspamd daemon in debug mode.";
       };
 
-      bindSocket = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}"
-        ];
-        defaultText = ''[
-          "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}"
-        ]'';
+      socketActivation = mkOption {
+        type = types.bool;
         description = ''
-          List of sockets to listen, in format acceptable by rspamd
+          Enable systemd socket activation for rspamd.
         '';
-        example = ''
-          bindSocket = [
-            "/run/rspamd.sock mode=0666 owner=rspamd"
-            "*:11333"
-          ];
+      };
+
+      workers = mkOption {
+        type = with types; attrsOf (submodule workerOpts);
+        description = ''
+          Attribute set of workers to start.
+        '';
+        default = {
+          normal = {};
+          controller = {};
+        };
+        example = literalExample ''
+          {
+            normal = {
+              includes = [ "$CONFDIR/worker-normal.inc" ];
+              bindSockets = [{
+                socket = "/run/rspamd/rspamd.sock";
+                mode = "0660";
+                owner = "${cfg.user}";
+                group = "${cfg.group}";
+              }];
+            };
+            controller = {
+              includes = [ "$CONFDIR/worker-controller.inc" ];
+              bindSockets = [ "[::1]:11334" ];
+            };
+          }
         '';
       };
 
-      bindUISocket = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "localhost:11334"
-        ];
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
         description = ''
-          List of sockets for web interface, in format acceptable by rspamd
+          Extra configuration to add at the end of the rspamd configuration
+          file.
         '';
       };
 
@@ -102,6 +272,13 @@ in
 
   config = mkIf cfg.enable {
 
+    services.rspamd.socketActivation = mkDefault (!opts.bindSocket.isDefined && !opts.bindUISocket.isDefined);
+
+    assertions = [ {
+      assertion = !cfg.socketActivation || !(opts.bindSocket.isDefined || opts.bindUISocket.isDefined);
+      message = "Can't use socketActivation for rspamd when using renamed bind socket options";
+    } ];
+
     # Allow users to run 'rspamc' and 'rspamadm'.
     environment.systemPackages = [ pkgs.rspamd ];
 
@@ -117,17 +294,22 @@ in
       gid = config.ids.gids.rspamd;
     };
 
+    environment.etc."rspamd.conf".source = rspamdConfFile;
+
     systemd.services.rspamd = {
       description = "Rspamd Service";
 
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
+      wantedBy = mkIf (!cfg.socketActivation) [ "multi-user.target" ];
+      after = [ "network.target" ] ++
+       (if cfg.socketActivation then allSocketNames else []);
+      requires = mkIf cfg.socketActivation allSocketNames;
 
       serviceConfig = {
         ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c ${rspamdConfFile} -f";
         Restart = "always";
         RuntimeDirectory = "rspamd";
         PrivateTmp = true;
+        Sockets = mkIf cfg.socketActivation (concatStringsSep " " allSocketNames);
       };
 
       preStart = ''
@@ -135,5 +317,25 @@ in
         ${pkgs.coreutils}/bin/chown ${cfg.user}:${cfg.group} /var/lib/rspamd
       '';
     };
+    systemd.sockets = mkIf cfg.socketActivation
+      (listToAttrs (map (each: {
+        name = "rspamd-${each.name}-${toString each.index}";
+        value = {
+          description = "Rspamd socket ${toString each.index} for worker ${each.name}";
+          wantedBy = [ "sockets.target" ];
+          listenStreams = (listenStreams each.value.socket);
+          socketConfig = {
+            BindIPv6Only = mkIf (isIPv6Socket each.value.socket) "ipv6-only";
+            Service = "rspamd.service";
+            SocketUser = mkIf (isUnixSocket each.value.socket) each.value.owner;
+            SocketGroup = mkIf (isUnixSocket each.value.socket) each.value.group;
+            SocketMode = mkIf (isUnixSocket each.value.socket) each.value.mode;
+          };
+        };
+      }) allMappedSockets));
   };
+  imports = [
+    (mkRenamedOptionModule [ "services" "rspamd" "bindSocket" ] [ "services" "rspamd" "workers" "normal" "bindSockets" ])
+    (mkRenamedOptionModule [ "services" "rspamd" "bindUISocket" ] [ "services" "rspamd" "workers" "controller" "bindSockets" ])
+  ];
 }
diff --git a/nixos/modules/services/misc/defaultUnicornConfig.rb b/nixos/modules/services/misc/defaultUnicornConfig.rb
index 84622622db70..0b58c59c7a51 100644
--- a/nixos/modules/services/misc/defaultUnicornConfig.rb
+++ b/nixos/modules/services/misc/defaultUnicornConfig.rb
@@ -1,205 +1,69 @@
-# The following was taken from github.com/crohr/syslogger and is BSD
-# licensed.
-require 'syslog'
-require 'logger'
-require 'thread'
+worker_processes 3
 
-class Syslogger
-
-  VERSION = "1.6.0"
-
-  attr_reader :level, :ident, :options, :facility, :max_octets
-  attr_accessor :formatter
-
-  MAPPING = {
-    Logger::DEBUG => Syslog::LOG_DEBUG,
-    Logger::INFO => Syslog::LOG_INFO,
-    Logger::WARN => Syslog::LOG_WARNING,
-    Logger::ERROR => Syslog::LOG_ERR,
-    Logger::FATAL => Syslog::LOG_CRIT,
-    Logger::UNKNOWN => Syslog::LOG_ALERT
-  }
-
-  #
-  # Initializes default options for the logger
-  # <tt>ident</tt>:: the name of your program [default=$0].
-  # <tt>options</tt>::  syslog options [default=<tt>Syslog::LOG_PID | Syslog::LOG_CONS</tt>].
-  #                     Correct values are:
-  #                       LOG_CONS    : writes the message on the console if an error occurs when sending the message;
-  #                       LOG_NDELAY  : no delay before sending the message;
-  #                       LOG_PERROR  : messages will also be written on STDERR;
-  #                       LOG_PID     : adds the process number to the message (just after the program name)
-  # <tt>facility</tt>:: the syslog facility [default=nil] Correct values include:
-  #                       Syslog::LOG_DAEMON
-  #                       Syslog::LOG_USER
-  #                       Syslog::LOG_SYSLOG
-  #                       Syslog::LOG_LOCAL2
-  #                       Syslog::LOG_NEWS
-  #                       etc.
-  #
-  # Usage:
-  #   logger = Syslogger.new("my_app", Syslog::LOG_PID | Syslog::LOG_CONS, Syslog::LOG_LOCAL0)
-  #   logger.level = Logger::INFO # use Logger levels
-  #   logger.warn "warning message"
-  #   logger.debug "debug message"
-  #
-  def initialize(ident = $0, options = Syslog::LOG_PID | Syslog::LOG_CONS, facility = nil)
-    @ident = ident
-    @options = options || (Syslog::LOG_PID | Syslog::LOG_CONS)
-    @facility = facility
-    @level = Logger::INFO
-    @mutex = Mutex.new
-    @formatter = Logger::Formatter.new
-  end
-
-  %w{debug info warn error fatal unknown}.each do |logger_method|
-    # Accepting *args as message could be nil.
-    #  Default params not supported in ruby 1.8.7
-    define_method logger_method.to_sym do |*args, &block|
-      return true if @level > Logger.const_get(logger_method.upcase)
-      message = args.first || block && block.call
-      add(Logger.const_get(logger_method.upcase), message)
-    end
-
-    unless logger_method == 'unknown'
-      define_method "#{logger_method}?".to_sym do
-        @level <= Logger.const_get(logger_method.upcase)
-      end
-    end
-  end
-
-  # Log a message at the Logger::INFO level. Useful for use with Rack::CommonLogger
-  def write(msg)
-    add(Logger::INFO, msg)
-  end
-
-  # Logs a message at the Logger::INFO level.
-  def <<(msg)
-    add(Logger::INFO, msg)
-  end
-
-  # Low level method to add a message.
-  # +severity+::  the level of the message. One of Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN
-  # +message+:: the message string.
-  #             If nil, the method will call the block and use the result as the message string.
-  #             If both are nil or no block is given, it will use the progname as per the behaviour of both the standard Ruby logger, and the Rails BufferedLogger.
-  # +progname+:: optionally, overwrite the program name that appears in the log message.
-  def add(severity, message = nil, progname = nil, &block)
-    if message.nil? && block.nil? && !progname.nil?
-      message, progname = progname, nil
-    end
-    progname ||= @ident
-
-    @mutex.synchronize do
-      Syslog.open(progname, @options, @facility) do |s|
-        s.mask = Syslog::LOG_UPTO(MAPPING[@level])
-        communication = clean(message || block && block.call)
-        if self.max_octets
-          buffer = "#{tags_text}"
-          communication.bytes do |byte|
-            buffer.concat(byte)
-            # if the last byte we added is potentially part of an escape, we'll go ahead and add another byte
-            if buffer.bytesize >= self.max_octets && !['%'.ord,'\\'.ord].include?(byte)
-              s.log(MAPPING[severity],buffer)
-              buffer = ""
-            end
-          end
-          s.log(MAPPING[severity],buffer) unless buffer.empty?
-        else
-          s.log(MAPPING[severity],"#{tags_text}#{communication}")
-        end
-      end
-    end
-  end
-
-  # Set the max octets of the messages written to the log
-  def max_octets=(max_octets)
-    @max_octets = max_octets
-  end
-
-  # Sets the minimum level for messages to be written in the log.
-  # +level+:: one of <tt>Logger::DEBUG</tt>, <tt>Logger::INFO</tt>, <tt>Logger::WARN</tt>, <tt>Logger::ERROR</tt>, <tt>Logger::FATAL</tt>, <tt>Logger::UNKNOWN</tt>
-  def level=(level)
-    level = Logger.const_get(level.to_s.upcase) if level.is_a?(Symbol)
-
-    unless level.is_a?(Fixnum)
-      raise ArgumentError.new("Invalid logger level `#{level.inspect}`")
-    end
-
-    @level = level
-  end
-
-  # Sets the ident string passed along to Syslog
-  def ident=(ident)
-    @ident = ident
-  end
-
-  # Tagging code borrowed from ActiveSupport gem
-  def tagged(*tags)
-    new_tags = push_tags(*tags)
-    yield self
-  ensure
-    pop_tags(new_tags.size)
-  end
-
-  def push_tags(*tags)
-    tags.flatten.reject{ |i| i.respond_to?(:empty?) ? i.empty? : !i }.tap do |new_tags|
-      current_tags.concat new_tags
-    end
-  end
-
-  def pop_tags(size = 1)
-    current_tags.pop size
-  end
-
-  def clear_tags!
-    current_tags.clear
-  end
-
-  protected
-
-  # Borrowed from SyslogLogger.
-  def clean(message)
-    message = message.to_s.dup
-    message.strip! # remove whitespace
-    message.gsub!(/\n/, '\\n') # escape newlines
-    message.gsub!(/%/, '%%') # syslog(3) freaks on % (printf)
-    message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes
-    message
-  end
-
-  private
-
-  def tags_text
-    tags = current_tags
-    if tags.any?
-      tags.collect { |tag| "[#{tag}] " }.join
-    end
-  end
-
-  def current_tags
-    Thread.current[:syslogger_tagged_logging_tags] ||= []
-  end
-end
+listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024
+listen "/run/gitlab/gitlab.socket", :backlog => 1024
 
-worker_processes 2
 working_directory ENV["GITLAB_PATH"]
-pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid"
 
-listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024
+pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid"
 
 timeout 60
 
-logger Syslogger.new
-
+# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
+# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
 preload_app true
-
 GC.respond_to?(:copy_on_write_friendly=) and
   GC.copy_on_write_friendly = true
 
 check_client_connection false
 
+before_fork do |server, worker|
+  # the following is highly recommended for Rails + "preload_app true"
+  # as there's no need for the master process to hold a connection
+  defined?(ActiveRecord::Base) and
+    ActiveRecord::Base.connection.disconnect!
+
+  # The following is only recommended for memory/DB-constrained
+  # installations.  It is not needed if your system can house
+  # twice as many worker_processes as you have configured.
+  #
+  # This allows a new master process to incrementally
+  # phase out the old master process with SIGTTOU to avoid a
+  # thundering herd (especially in the "preload_app false" case)
+  # when doing a transparent upgrade.  The last worker spawned
+  # will then kill off the old master process with a SIGQUIT.
+  old_pid = "#{server.config[:pid]}.oldbin"
+  if old_pid != server.pid
+    begin
+      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
+      Process.kill(sig, File.read(old_pid).to_i)
+    rescue Errno::ENOENT, Errno::ESRCH
+    end
+  end
+
+  # Throttle the master from forking too quickly by sleeping.  Due
+  # to the implementation of standard Unix signal handlers, this
+  # helps (but does not completely) prevent identical, repeated signals
+  # from being lost when the receiving process is busy.
+  # sleep 1
+end
+
 after_fork do |server, worker|
+  # per-process listener ports for debugging/admin/migrations
+  # addr = "127.0.0.1:#{9293 + worker.nr}"
+  # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
+
+  # the following is *required* for Rails + "preload_app true",
   defined?(ActiveRecord::Base) and
     ActiveRecord::Base.establish_connection
+
+  # reset prometheus client, this will cause any opened metrics files to be closed
+  defined?(::Prometheus::Client.reinitialize_on_pid_change) &&
+    Prometheus::Client.reinitialize_on_pid_change
+
+  # if preload_app is true, then you may also want to check and
+  # restart any other shared sockets/descriptors such as Memcached,
+  # and Redis.  TokyoCabinet file handles are safe to reuse
+  # between any number of forked children (assuming your kernel
+  # correctly implements pread()/pwrite() system calls)
 end
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index e96645c79c77..e4517c636e88 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -32,11 +32,17 @@ in
         description = "Whether to enable Disnix";
       };
 
+      enableMultiUser = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Whether to support multi-user mode by enabling the Disnix D-Bus service";
+      };
+
       useWebServiceInterface = mkOption {
         default = false;
         description = "Whether to enable the DisnixWebService interface running on Apache Tomcat";
       };
-      
+
       package = mkOption {
         type = types.path;
         description = "The Disnix package";
@@ -51,8 +57,8 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-    dysnomia.enable = true;
-    
+    services.dysnomia.enable = true;
+
     environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
     services.dbus.enable = true;
@@ -71,7 +77,7 @@ in
       };
 
     systemd.services = {
-      disnix = {
+      disnix = mkIf cfg.enableMultiUser {
         description = "Disnix server";
         wants = [ "dysnomia.target" ];
         wantedBy = [ "multi-user.target" ];
@@ -92,7 +98,7 @@ in
         }
         // (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";
       };
 
diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix
index df44d0a54866..9e66e0811ab7 100644
--- a/nixos/modules/services/misc/dysnomia.nix
+++ b/nixos/modules/services/misc/dysnomia.nix
@@ -3,8 +3,8 @@
 with lib;
 
 let
-  cfg = config.dysnomia;
-  
+  cfg = config.services.dysnomia;
+
   printProperties = properties:
     concatMapStrings (propertyName:
       let
@@ -13,7 +13,7 @@ let
       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 = ''
@@ -22,13 +22,13 @@ let
       EOF
     '';
   };
-  
+
   containersDir = pkgs.stdenv.mkDerivation {
     name = "dysnomia-containers";
     buildCommand = ''
       mkdir -p $out
       cd $out
-      
+
       ${concatMapStrings (containerName:
         let
           containerProperties = cfg.containers."${containerName}";
@@ -42,11 +42,11 @@ let
       ) (builtins.attrNames cfg.containers)}
     '';
   };
-  
+
   linkMutableComponents = {containerName}:
     ''
       mkdir ${containerName}
-      
+
       ${concatMapStrings (componentName:
         let
           component = cfg.components."${containerName}"."${componentName}";
@@ -54,13 +54,13 @@ let
         "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}";
@@ -72,59 +72,59 @@ let
 in
 {
   options = {
-    dysnomia = {
-      
+    services.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;
@@ -136,16 +136,16 @@ in
         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: {
+
+    services.dysnomia.package = pkgs.dysnomia.override (origArgs: {
       enableApacheWebApplication = config.services.httpd.enable;
       enableAxis2WebService = config.services.tomcat.axis2.enable;
       enableEjabberdDump = config.services.ejabberd.enable;
@@ -155,10 +155,10 @@ in
       enableTomcatWebApplication = config.services.tomcat.enable;
       enableMongoDatabase = config.services.mongodb.enable;
     });
-    
-    dysnomia.properties = {
+
+    services.dysnomia.properties = {
       hostname = config.networking.hostName;
-      system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
+      inherit (config.nixpkgs.localSystem) system;
 
       supportedTypes = (import "${pkgs.stdenv.mkDerivation {
         name = "supportedtypes";
@@ -173,8 +173,8 @@ in
         '';
       }}");
     };
-    
-    dysnomia.containers = lib.recursiveUpdate ({
+
+    services.dysnomia.containers = lib.recursiveUpdate ({
       process = {};
       wrapper = {};
     }
@@ -192,9 +192,11 @@ in
         mysqlPassword = builtins.readFile (config.services.mysql.rootPassword);
       };
     }
-    // lib.optionalAttrs (config.services.postgresql.enable && cfg.enableAuthentication) { postgresql-database = {
-      postgresqlUsername = "root";
-    }; }
+    // lib.optionalAttrs (config.services.postgresql.enable) { postgresql-database = {
+      } // lib.optionalAttrs (cfg.enableAuthentication) {
+        postgresqlUsername = "postgres";
+      };
+    }
     // lib.optionalAttrs (config.services.tomcat.enable) { tomcat-webapplication = {
       tomcatPort = 8080;
     }; }
diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix
index 053e7e95635f..164221cbab7f 100644
--- a/nixos/modules/services/misc/folding-at-home.nix
+++ b/nixos/modules/services/misc/folding-at-home.nix
@@ -57,7 +57,7 @@ in {
         chown ${fahUser} ${stateDir}
         cp -f ${pkgs.writeText "client.cfg" cfg.config} ${stateDir}/client.cfg
       '';
-      script = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
+      script = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
     };
 
     services.foldingAtHome.config = ''
diff --git a/nixos/modules/services/misc/geoip-updater.nix b/nixos/modules/services/misc/geoip-updater.nix
index 760fa66e80d6..e0b9df96f8e8 100644
--- a/nixos/modules/services/misc/geoip-updater.nix
+++ b/nixos/modules/services/misc/geoip-updater.nix
@@ -14,7 +14,7 @@ let
   # ExecStart= command with '@' doesn't work because we start a shell (new
   # process) that creates a new argv[0].)
   geoip-updater = pkgs.writeScriptBin "geoip-updater" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     skipExisting=0
     debug()
     {
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index f0b44b7bedeb..63e976ae566c 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
   cfg = config.services.gitea;
+  pg = config.services.postgresql;
+  usePostgresql = cfg.database.type == "postgres";
   configFile = pkgs.writeText "app.ini" ''
     APP_NAME = ${cfg.appName}
     RUN_USER = ${cfg.user}
@@ -16,6 +18,9 @@ let
     USER = ${cfg.database.user}
     PASSWD = #dbpass#
     PATH = ${cfg.database.path}
+    ${optionalString usePostgresql ''
+      SSL_MODE = disable
+    ''}
 
     [repository]
     ROOT = ${cfg.repositoryRoot}
@@ -35,6 +40,10 @@ let
     SECRET_KEY = #secretkey#
     INSTALL_LOCK = true
 
+    [log]
+    ROOT_PATH = ${cfg.log.rootPath}
+    LEVEL = ${cfg.log.level}
+
     ${cfg.extraConfig}
   '';
 in
@@ -60,6 +69,19 @@ in
         description = "gitea data directory.";
       };
 
+      log = {
+        rootPath = mkOption {
+          default = "${cfg.stateDir}/log";
+          type = types.str;
+          description = "Root path for log files.";
+        };
+        level = mkOption {
+          default = "Trace";
+          type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ];
+          description = "General log level.";
+        };
+      };
+
       user = mkOption {
         type = types.str;
         default = "gitea";
@@ -82,7 +104,7 @@ in
 
         port = mkOption {
           type = types.int;
-          default = 3306;
+          default = (if !usePostgresql then 3306 else pg.port);
           description = "Database host port.";
         };
 
@@ -123,6 +145,15 @@ in
           default = "${cfg.stateDir}/data/gitea.db";
           description = "Path to the sqlite3 database file.";
         };
+
+        createDatabase = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to create a local postgresql database automatically.
+            This only applies if database type "postgres" is selected.
+          '';
+        };
       };
 
       appName = mkOption {
@@ -186,10 +217,11 @@ in
   };
 
   config = mkIf cfg.enable {
+    services.postgresql.enable = mkIf usePostgresql (mkDefault true);
 
     systemd.services.gitea = {
       description = "gitea";
-      after = [ "network.target" ];
+      after = [ "network.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.gitea.bin ];
 
@@ -231,12 +263,31 @@ in
           mkdir -p ${cfg.stateDir}/conf
           cp -r ${pkgs.gitea.out}/locale ${cfg.stateDir}/conf/locale
         fi
+      '' + optionalString (usePostgresql && cfg.database.createDatabase) ''
+        if ! test -e "${cfg.stateDir}/db-created"; then
+          echo "CREATE ROLE ${cfg.database.user}
+                  WITH ENCRYPTED PASSWORD '$(head -n1 ${cfg.database.passwordFile})'
+                  NOCREATEDB NOCREATEROLE LOGIN"   |
+            ${pkgs.sudo}/bin/sudo -u ${pg.superUser} ${pg.package}/bin/psql
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createdb             \
+            --owner=${cfg.database.user}           \
+            --encoding=UTF8                        \
+            --lc-collate=C                         \
+            --lc-ctype=C                           \
+            --template=template0                   \
+            ${cfg.database.name}
+          touch "${cfg.stateDir}/db-created"
+        fi
+      '' + ''
+        chown ${cfg.user} -R ${cfg.stateDir}
       '';
 
       serviceConfig = {
         Type = "simple";
         User = cfg.user;
         WorkingDirectory = cfg.stateDir;
+        PermissionsStartOnly = true;
         ExecStart = "${pkgs.gitea.bin}/bin/gitea web";
         Restart = "always";
       };
@@ -253,6 +304,7 @@ in
         description = "Gitea Service";
         home = cfg.stateDir;
         createHome = true;
+        useDefaultShell = true;
       };
     };
 
diff --git a/nixos/modules/services/misc/gitit.nix b/nixos/modules/services/misc/gitit.nix
index 44880ebeda14..94a98e0335df 100644
--- a/nixos/modules/services/misc/gitit.nix
+++ b/nixos/modules/services/misc/gitit.nix
@@ -17,7 +17,7 @@ let
   gititSh = hsPkgs: extras: with pkgs; let
     env = gititWithPkgs hsPkgs extras;
   in writeScript "gitit" ''
-    #!${stdenv.shell}
+    #!${runtimeShell}
     cd $HOME
     export NIX_GHC="${env}/bin/ghc"
     export NIX_GHCPKG="${env}/bin/ghc-pkg"
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 7b2b40e59232..20d7ec90dcc9 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -29,8 +29,12 @@ let
 
   gitalyToml = pkgs.writeText "gitaly.toml" ''
     socket_path = "${lib.escape ["\""] gitalySocket}"
+    bin_dir = "${cfg.packages.gitaly}/bin"
     prometheus_listen_addr = "localhost:9236"
 
+    [git]
+    bin_path = "${pkgs.git}/bin/git"
+
     [gitaly-ruby]
     dir = "${cfg.packages.gitaly.ruby}"
 
@@ -70,7 +74,7 @@ let
       secret_key_base: ${cfg.secrets.secret}
       otp_key_base: ${cfg.secrets.otp}
       db_key_base: ${cfg.secrets.db}
-      jws_private_key: ${builtins.toJSON cfg.secrets.jws}
+      openid_connect_signing_key: ${builtins.toJSON cfg.secrets.jws}
   '';
 
   gitlabConfig = {
@@ -104,6 +108,7 @@ let
       ldap.enabled = false;
       omniauth.enabled = false;
       shared.path = "${cfg.statePath}/shared";
+      gitaly.client_path = "${cfg.packages.gitaly}/bin";
       backup.path = "${cfg.backupPath}";
       gitlab_shell = {
         path = "${cfg.packages.gitlab-shell}";
@@ -117,8 +122,6 @@ let
       };
       git = {
         bin_path = "git";
-        max_size = 20971520; # 20MB
-        timeout = 10;
       };
       monitoring = {
         ip_whitelist = [ "127.0.0.0/8" "::1/128" ];
@@ -140,6 +143,7 @@ let
     GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
     GITLAB_STATE_PATH = "${cfg.statePath}";
     GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
+    SCHEMA = "${cfg.statePath}/db/schema.rb";
     GITLAB_LOG_PATH = "${cfg.statePath}/log";
     GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}";
     GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
@@ -248,7 +252,6 @@ in {
 
       databasePassword = mkOption {
         type = types.str;
-        default = "";
         description = "Gitlab database user password.";
       };
 
@@ -440,12 +443,6 @@ in {
 
     environment.systemPackages = [ pkgs.git gitlab-rake cfg.packages.gitlab-shell ];
 
-    assertions = [
-      { assertion = cfg.databasePassword != "";
-        message = "databasePassword must be set";
-      }
-    ];
-
     # Redis is required for the sidekiq queue runner.
     services.redis.enable = mkDefault true;
     # We use postgres as the main data store.
@@ -496,13 +493,15 @@ in {
       after = [ "network.target" "gitlab.service" ];
       wantedBy = [ "multi-user.target" ];
       environment.HOME = gitlabEnv.HOME;
-      path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv ];
+      environment.GEM_HOME = "${cfg.packages.gitaly.rubyEnv}/${ruby.gemPath}";
+      environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
+      path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv ruby ];
       serviceConfig = {
         #PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
         User = cfg.user;
         Group = cfg.group;
-        TimeoutSec = "300";
+        TimeoutSec = "infinity";
         Restart = "on-failure";
         WorkingDirectory = gitlabEnv.HOME;
         ExecStart = "${cfg.packages.gitaly}/bin/gitaly ${gitalyToml}";
@@ -568,6 +567,7 @@ in {
         mkdir -p ${cfg.statePath}/tmp/pids
         mkdir -p ${cfg.statePath}/tmp/sockets
         mkdir -p ${cfg.statePath}/shell
+        mkdir -p ${cfg.statePath}/db
 
         rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks
         mkdir -p ${cfg.statePath}/config
@@ -582,6 +582,7 @@ in {
         ln -sf ${cfg.statePath}/log /run/gitlab/log
         ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
         ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
+        ln -sf $GITLAB_SHELL_CONFIG_PATH /run/gitlab/shell-config.yml
         chown -R ${cfg.user}:${cfg.group} /run/gitlab
 
         # Prepare home directory
@@ -589,6 +590,7 @@ in {
         touch ${gitlabEnv.HOME}/.ssh/authorized_keys
         chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/
 
+        cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
         cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
         ${optionalString cfg.smtp.enable ''
           ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
diff --git a/nixos/modules/services/misc/gitolite.nix b/nixos/modules/services/misc/gitolite.nix
index f395b9558b5a..6e60316d000c 100644
--- a/nixos/modules/services/misc/gitolite.nix
+++ b/nixos/modules/services/misc/gitolite.nix
@@ -207,7 +207,7 @@ in
             gitolite setup -pk ${pubkeyFile}
           fi
           if [ -n "${hooks}" ]; then
-            cp ${hooks} .gitolite/hooks/common/
+            cp -f ${hooks} .gitolite/hooks/common/
             chmod +x .gitolite/hooks/common/*
           fi
           gitolite setup # Upgrade if needed
diff --git a/nixos/modules/services/misc/gitweb.nix b/nixos/modules/services/misc/gitweb.nix
new file mode 100644
index 000000000000..ca21366b7796
--- /dev/null
+++ b/nixos/modules/services/misc/gitweb.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gitweb;
+
+in
+{
+
+  options.services.gitweb = {
+
+    projectroot = mkOption {
+      default = "/srv/git";
+      type = types.path;
+      description = ''
+        Path to git projects (bare repositories) that should be served by
+        gitweb. Must not end with a slash.
+      '';
+    };
+
+    extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      description = ''
+        Verbatim configuration text appended to the generated gitweb.conf file.
+      '';
+      example = ''
+        $feature{'highlight'}{'default'} = [1];
+        $feature{'ctags'}{'default'} = [1];
+        $feature{'avatar'}{'default'} = ['gravatar'];
+      '';
+    };
+
+    gitwebTheme = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Use an alternative theme for gitweb, strongly inspired by GitHub.
+      '';
+    };
+
+    gitwebConfigFile = mkOption {
+      default = pkgs.writeText "gitweb.conf" ''
+        # path to git projects (<project>.git)
+        $projectroot = "${cfg.projectroot}";
+        $highlight_bin = "${pkgs.highlight}/bin/highlight";
+        ${cfg.extraConfig}
+      '';
+      type = types.path;
+      readOnly = true;
+      internal = true;
+    };
+
+  };
+
+  meta.maintainers = with maintainers; [ gnidorah ];
+
+}
diff --git a/nixos/modules/services/misc/gogs.nix b/nixos/modules/services/misc/gogs.nix
index f6d326e43d94..ba744d37e71c 100644
--- a/nixos/modules/services/misc/gogs.nix
+++ b/nixos/modules/services/misc/gogs.nix
@@ -35,6 +35,9 @@ let
     SECRET_KEY = #secretkey#
     INSTALL_LOCK = true
 
+    [log]
+    ROOT_PATH = ${cfg.stateDir}/log
+
     ${cfg.extraConfig}
   '';
 in
diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix
index 81fb024f9094..0888221ab62f 100644
--- a/nixos/modules/services/misc/gollum.nix
+++ b/nixos/modules/services/misc/gollum.nix
@@ -32,6 +32,24 @@ in
       description = "Content of the configuration file";
     };
 
+    mathjax = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable support for math rendering using MathJax";
+    };
+
+    allowUploads = mkOption {
+      type = types.nullOr (types.enum [ "dir" "page" ]);
+      default = null;
+      description = "Enable uploads of external files";
+    };
+
+    emoji = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Parse and interpret emoji tags";
+    };
+
     branch = mkOption {
       type = types.str;
       default = "master";
@@ -84,6 +102,9 @@ in
             --host ${cfg.address} \
             --config ${builtins.toFile "gollum-config.rb" cfg.extraConfig} \
             --ref ${cfg.branch} \
+            ${optionalString cfg.mathjax "--mathjax"} \
+            ${optionalString cfg.emoji "--emoji"} \
+            ${optionalString (cfg.allowUploads != null) "--allow-uploads ${cfg.allowUploads}"} \
             ${cfg.stateDir}
         '';
       };
diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix
new file mode 100644
index 000000000000..ac37c11106ef
--- /dev/null
+++ b/nixos/modules/services/misc/home-assistant.nix
@@ -0,0 +1,144 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.home-assistant;
+
+  configFile = pkgs.writeText "configuration.yaml" (builtins.toJSON cfg.config);
+
+  availableComponents = pkgs.home-assistant.availableComponents;
+
+  # Given component "parentConfig.platform", returns whether config.parentConfig
+  # is a list containing a set with set.platform == "platform".
+  #
+  # For example, the component sensor.luftdaten is used as follows:
+  # config.sensor = [ {
+  #   platform = "luftdaten";
+  #   ...
+  # } ];
+  useComponentPlatform = component:
+    let
+      path = splitString "." component;
+      parentConfig = attrByPath (init path) null cfg.config;
+      platform = last path;
+    in isList parentConfig && any
+      (item: item.platform or null == platform)
+      parentConfig;
+
+  # Returns whether component is used in config
+  useComponent = component:
+    hasAttrByPath (splitString "." component) cfg.config
+    || useComponentPlatform component;
+
+  # List of components used in config
+  extraComponents = filter useComponent availableComponents;
+
+  package = if cfg.autoExtraComponents
+    then (cfg.package.override { inherit extraComponents; })
+    else cfg.package;
+
+in {
+  meta.maintainers = with maintainers; [ dotlambda ];
+
+  options.services.home-assistant = {
+    enable = mkEnableOption "Home Assistant";
+
+    configDir = mkOption {
+      default = "/var/lib/hass";
+      type = types.path;
+      description = "The config directory, where your <filename>configuration.yaml</filename> is located.";
+    };
+
+    config = mkOption {
+      default = null;
+      type = with types; nullOr attrs;
+      example = literalExample ''
+        {
+          homeassistant = {
+            name = "Home";
+            time_zone = "UTC";
+          };
+          frontend = { };
+          http = { };
+          feedreader.urls = [ "https://nixos.org/blogs.xml" ];
+        }
+      '';
+      description = ''
+        Your <filename>configuration.yaml</filename> as a Nix attribute set.
+        Beware that setting this option will delete your previous <filename>configuration.yaml</filename>.
+      '';
+    };
+
+    package = mkOption {
+      default = pkgs.home-assistant;
+      defaultText = "pkgs.home-assistant";
+      type = types.package;
+      example = literalExample ''
+        pkgs.home-assistant.override {
+          extraPackages = ps: with ps; [ colorlog ];
+        }
+      '';
+      description = ''
+        Home Assistant package to use.
+        Override <literal>extraPackages</literal> in order to add additional dependencies.
+      '';
+    };
+
+    autoExtraComponents = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        If set to <literal>true</literal>, the components used in <literal>config</literal>
+        are set as the specified package's <literal>extraComponents</literal>.
+        This in turn adds all packaged dependencies to the derivation.
+        You might still see import errors in your log.
+        In this case, you will need to package the necessary dependencies yourself
+        or ask for someone else to package them.
+        If a dependency is packaged but not automatically added to this list,
+        you might need to specify it in <literal>extraPackages</literal>.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.home-assistant = {
+      description = "Home Assistant";
+      after = [ "network.target" ];
+      preStart = lib.optionalString (cfg.config != null) ''
+        rm -f ${cfg.configDir}/configuration.yaml
+        ln -s ${configFile} ${cfg.configDir}/configuration.yaml
+      '';
+      serviceConfig = {
+        ExecStart = ''
+          ${package}/bin/hass --config "${cfg.configDir}"
+        '';
+        User = "hass";
+        Group = "hass";
+        Restart = "on-failure";
+        ProtectSystem = "strict";
+        ReadWritePaths = "${cfg.configDir}";
+        PrivateTmp = true;
+      };
+      path = [
+        "/run/wrappers" # needed for ping
+      ];
+    };
+
+    systemd.targets.home-assistant = rec {
+      description = "Home Assistant";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "home-assistant.service" ];
+      after = wants;
+    };
+
+    users.extraUsers.hass = {
+      home = cfg.configDir;
+      createHome = true;
+      group = "hass";
+      uid = config.ids.uids.hass;
+    };
+
+    users.extraGroups.hass.gid = config.ids.gids.hass;
+  };
+}
diff --git a/nixos/modules/services/misc/ihaskell.nix b/nixos/modules/services/misc/ihaskell.nix
index e07a4a44613a..6da9cc8c47e6 100644
--- a/nixos/modules/services/misc/ihaskell.nix
+++ b/nixos/modules/services/misc/ihaskell.nix
@@ -55,7 +55,7 @@ in
       serviceConfig = {
         User = config.users.extraUsers.ihaskell.name;
         Group = config.users.extraGroups.ihaskell.name;
-        ExecStart = "${pkgs.stdenv.shell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\"";
+        ExecStart = "${pkgs.runtimeShell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\"";
       };
     };
   };
diff --git a/nixos/modules/services/misc/logkeys.nix b/nixos/modules/services/misc/logkeys.nix
index 6051c884465d..df0b3ae24c90 100644
--- a/nixos/modules/services/misc/logkeys.nix
+++ b/nixos/modules/services/misc/logkeys.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index a3ec0ea59f8f..7e880ad09b89 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
   cfg = config.services.matrix-synapse;
+  pg = config.services.postgresql;
+  usePostgresql = cfg.database_type == "psycopg2";
   logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
   mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${boolToString r.compress}}'';
   mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${boolToString l.tls}, x_forwarded: ${boolToString l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
@@ -38,13 +40,12 @@ database: {
   name: "${cfg.database_type}",
   args: {
     ${concatStringsSep ",\n    " (
-      mapAttrsToList (n: v: "\"${n}\": ${v}") cfg.database_args
+      mapAttrsToList (n: v: "\"${n}\": ${builtins.toJSON v}") cfg.database_args
     )}
   }
 }
 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}
@@ -53,8 +54,8 @@ 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"
+media_store_path: "${cfg.dataDir}/media"
+uploads_path: "${cfg.dataDir}/uploads"
 max_upload_size: "${cfg.max_upload_size}"
 max_image_pixels: "${cfg.max_image_pixels}"
 dynamic_thumbnails: ${boolToString cfg.dynamic_thumbnails}
@@ -86,7 +87,7 @@ ${optionalString (cfg.macaroon_secret_key != null) ''
 expire_access_token: ${boolToString cfg.expire_access_token}
 enable_metrics: ${boolToString cfg.enable_metrics}
 report_stats: ${boolToString cfg.report_stats}
-signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key"
+signing_key_path: "${cfg.dataDir}/homeserver.signing.key"
 key_refresh_interval: "${cfg.key_refresh_interval}"
 perspectives:
   servers: {
@@ -156,7 +157,7 @@ in {
       tls_certificate_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.crt";
+        example = "${cfg.dataDir}/homeserver.tls.crt";
         description = ''
           PEM encoded X509 certificate for TLS.
           You can replace the self-signed certificate that synapse
@@ -168,7 +169,7 @@ in {
       tls_private_key_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.key";
+        example = "${cfg.dataDir}/homeserver.tls.key";
         description = ''
           PEM encoded private key for TLS. Specify null if synapse is not
           speaking TLS directly.
@@ -177,7 +178,7 @@ in {
       tls_dh_params_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.dh";
+        example = "${cfg.dataDir}/homeserver.tls.dh";
         description = ''
           PEM dh parameters for ephemeral keys
         '';
@@ -185,6 +186,7 @@ in {
       server_name = mkOption {
         type = types.str;
         example = "example.com";
+        default = config.networking.hostName;
         description = ''
           The domain name of the server, with optional explicit port.
           This is used by remote servers to connect to this server,
@@ -340,16 +342,39 @@ in {
       };
       database_type = mkOption {
         type = types.enum [ "sqlite3" "psycopg2" ];
-        default = "sqlite3";
+        default = if versionAtLeast config.system.stateVersion "18.03"
+          then "psycopg2"
+          else "sqlite3";
         description = ''
           The database engine name. Can be sqlite or psycopg2.
         '';
       };
+      create_local_database = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to create a local database automatically.
+        '';
+      };
+      database_name = mkOption {
+        type = types.str;
+        default = "matrix-synapse";
+        description = "Database name.";
+      };
+      database_user = mkOption {
+        type = types.str;
+        default = "matrix-synapse";
+        description = "Database user name.";
+      };
       database_args = mkOption {
         type = types.attrs;
         default = {
-          database = "/var/lib/matrix-synapse/homeserver.db";
-        };
+          sqlite3 = { database = "${cfg.dataDir}/homeserver.db"; };
+          psycopg2 = {
+            user = cfg.database_user;
+            database = cfg.database_name;
+          };
+        }."${cfg.database_type}";
         description = ''
           Arguments to pass to the engine.
         '';
@@ -579,6 +604,18 @@ in {
           Extra config options for matrix-synapse.
         '';
       };
+      extraConfigFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          Extra config files to include.
+
+          The configuration files will be included based on the command line
+          argument --config-path. This allows to configure secrets without
+          having to go through the Nix store, e.g. based on deployment keys if
+          NixOPS is in use.
+        '';
+      };
       logConfig = mkOption {
         type = types.lines;
         default = readFile ./matrix-synapse-log_config.yaml;
@@ -586,6 +623,14 @@ in {
           A yaml python logging config file
         '';
       };
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/matrix-synapse";
+        description = ''
+          The directory where matrix-synapse stores its stateful data such as
+          certificates, media and uploads.
+        '';
+      };
     };
   };
 
@@ -593,7 +638,7 @@ in {
     users.extraUsers = [
       { name = "matrix-synapse";
         group = "matrix-synapse";
-        home = "/var/lib/matrix-synapse";
+        home = cfg.dataDir;
         createHome = true;
         shell = "${pkgs.bash}/bin/bash";
         uid = config.ids.uids.matrix-synapse;
@@ -604,23 +649,48 @@ in {
         gid = config.ids.gids.matrix-synapse;
       } ];
 
+    services.postgresql.enable = mkIf usePostgresql (mkDefault true);
+
     systemd.services.matrix-synapse = {
       description = "Synapse Matrix homeserver";
-      after = [ "network.target" ];
+      after = [ "network.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
         ${cfg.package}/bin/homeserver \
           --config-path ${configFile} \
-          --keys-directory /var/lib/matrix-synapse \
+          --keys-directory ${cfg.dataDir} \
           --generate-keys
+      '' + optionalString (usePostgresql && cfg.create_local_database) ''
+        if ! test -e "${cfg.dataDir}/db-created"; then
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createuser \
+            --login \
+            --no-createdb \
+            --no-createrole \
+            --encrypted \
+            ${cfg.database_user}
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createdb \
+            --owner=${cfg.database_user} \
+            --encoding=UTF8 \
+            --lc-collate=C \
+            --lc-ctype=C \
+            --template=template0 \
+            ${cfg.database_name}
+          touch "${cfg.dataDir}/db-created"
+        fi
       '';
       serviceConfig = {
         Type = "simple";
         User = "matrix-synapse";
         Group = "matrix-synapse";
-        WorkingDirectory = "/var/lib/matrix-synapse";
+        WorkingDirectory = cfg.dataDir;
         PermissionsStartOnly = true;
-        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse";
+        ExecStart = ''
+          ${cfg.package}/bin/homeserver \
+            ${ concatMapStringsSep "\n  " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
+            --keys-directory ${cfg.dataDir}
+        '';
         Restart = "on-failure";
       };
     };
diff --git a/nixos/modules/services/misc/mbpfan.nix b/nixos/modules/services/misc/mbpfan.nix
index 972d8b572d36..50f6f80ad00c 100644
--- a/nixos/modules/services/misc/mbpfan.nix
+++ b/nixos/modules/services/misc/mbpfan.nix
@@ -8,13 +8,7 @@ let
 
 in {
   options.services.mbpfan = {
-    enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Whether to enable the mbpfan daemon.
-      '';
-    };
+    enable = mkEnableOption "mbpfan, fan controller daemon for Apple Macs and MacBooks";
 
     package = mkOption {
       type = types.package;
diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix
index 47be10274d3b..effa29b64f63 100644
--- a/nixos/modules/services/misc/mesos-slave.nix
+++ b/nixos/modules/services/misc/mesos-slave.nix
@@ -188,7 +188,7 @@ in {
       description = "Mesos Slave";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      path = [ pkgs.stdenv.shellPackage ];
+      path = [ pkgs.runtimeShellPackage ];
       serviceConfig = {
         ExecStart = ''
           ${pkgs.mesos}/bin/mesos-slave \
@@ -213,7 +213,7 @@ in {
         PermissionsStartOnly = true;
       };
       preStart = ''
-        mkdir -m 0700 -p ${cfg.workDir}
+        mkdir -m 0701 -p ${cfg.workDir}
       '';
     };
   };
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index beca820d2d60..f2d34560a718 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -8,7 +8,7 @@ let
 
   nix = cfg.package.out;
 
-  isNix112 = versionAtLeast (getVersion nix) "1.12pre";
+  isNix20 = versionAtLeast (getVersion nix) "2.0pre";
 
   makeNixBuildUser = nr:
     { name = "nixbld${toString nr}";
@@ -26,32 +26,40 @@ let
 
   nixConf =
     let
-      # If we're using sandbox for builds, then provide /bin/sh in
-      # the sandbox as a bind-mount to bash. This means we also need to
-      # include the entire closure of bash.
-      sh = pkgs.stdenv.shell;
+      # In Nix < 2.0, If we're using sandbox for builds, then provide
+      # /bin/sh in the sandbox as a bind-mount to bash. This means we
+      # also need to include the entire closure of bash. Nix >= 2.0
+      # provides a /bin/sh by default.
+      sh = pkgs.runtimeShell;
       binshDeps = pkgs.writeReferencesToFile sh;
     in
-      pkgs.runCommand "nix.conf" {extraOptions = cfg.extraOptions; } ''
-        extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
+      pkgs.runCommand "nix.conf" { extraOptions = cfg.extraOptions; } ''
+        ${optionalString (!isNix20) ''
+          extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
+        ''}
         cat > $out <<END
         # WARNING: this file is generated from the nix.* options in
         # your NixOS configuration, typically
         # /etc/nixos/configuration.nix.  Do not edit it!
         build-users-group = nixbld
-        build-max-jobs = ${toString (cfg.maxJobs)}
-        build-cores = ${toString (cfg.buildCores)}
-        build-use-sandbox = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
-        build-sandbox-paths = ${toString cfg.sandboxPaths} /bin/sh=${sh} $(echo $extraPaths)
-        binary-caches = ${toString cfg.binaryCaches}
-        trusted-binary-caches = ${toString cfg.trustedBinaryCaches}
-        binary-cache-public-keys = ${toString cfg.binaryCachePublicKeys}
+        ${if isNix20 then "max-jobs" else "build-max-jobs"} = ${toString (cfg.maxJobs)}
+        ${if isNix20 then "cores" else "build-cores"} = ${toString (cfg.buildCores)}
+        ${if isNix20 then "sandbox" else "build-use-sandbox"} = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
+        ${if isNix20 then "extra-sandbox-paths" else "build-sandbox-paths"} = ${toString cfg.sandboxPaths} ${optionalString (!isNix20) "/bin/sh=${sh} $(echo $extraPaths)"}
+        ${if isNix20 then "substituters" else "binary-caches"} = ${toString cfg.binaryCaches}
+        ${if isNix20 then "trusted-substituters" else "trusted-binary-caches"} = ${toString cfg.trustedBinaryCaches}
+        ${if isNix20 then "trusted-public-keys" else "binary-cache-public-keys"} = ${toString cfg.binaryCachePublicKeys}
         auto-optimise-store = ${boolToString cfg.autoOptimiseStore}
-        ${optionalString cfg.requireSignedBinaryCaches ''
-          signed-binary-caches = *
+        ${if isNix20 then ''
+          require-sigs = ${if cfg.requireSignedBinaryCaches then "true" else "false"}
+        '' else ''
+          signed-binary-caches = ${if cfg.requireSignedBinaryCaches then "*" else ""}
         ''}
         trusted-users = ${toString cfg.trustedUsers}
         allowed-users = ${toString cfg.allowedUsers}
+        ${optionalString (isNix20 && !cfg.distributedBuilds) ''
+          builders =
+        ''}
         $extraOptions
         END
       '';
@@ -377,8 +385,9 @@ in
     systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
 
     systemd.services.nix-daemon =
-      { path = [ nix pkgs.openssl.bin pkgs.utillinux config.programs.ssh.package ]
-          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
+      { path = [ nix pkgs.utillinux ]
+          ++ optionals cfg.distributedBuilds [ config.programs.ssh.package pkgs.gzip ]
+          ++ optionals (!isNix20) [ pkgs.openssl.bin ];
 
         environment = cfg.envVars
           // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
@@ -396,10 +405,9 @@ in
       };
 
     nix.envVars =
-      { NIX_CONF_DIR = "/etc/nix";
-      }
+      optionalAttrs (!isNix20) {
+        NIX_CONF_DIR = "/etc/nix";
 
-      // optionalAttrs (!isNix112) {
         # Enable the copy-from-other-stores substituter, which allows
         # builds to be sped up by copying build results from remote
         # Nix stores.  To do this, mount the remote file system on a
@@ -407,12 +415,8 @@ in
         NIX_OTHER_STORES = "/run/nix/remote-stores/*/nix";
       }
 
-      // optionalAttrs cfg.distributedBuilds {
-        NIX_BUILD_HOOK =
-          if isNix112 then
-            "${nix}/libexec/nix/build-remote"
-          else
-            "${nix}/libexec/nix/build-remote.pl";
+      // optionalAttrs (cfg.distributedBuilds && !isNix20) {
+        NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote.pl";
       };
 
     # Set up the environment variables for running Nix.
@@ -420,7 +424,7 @@ in
       { NIX_PATH = concatStringsSep ":" cfg.nixPath;
       };
 
-    environment.extraInit =
+    environment.extraInit = optionalString (!isNix20)
       ''
         # Set up secure multi-user builds: non-root users build through the
         # Nix daemon.
@@ -435,19 +439,18 @@ in
 
     services.xserver.displayManager.hiddenUsers = map ({ name, ... }: name) nixbldUsers;
 
+    # FIXME: use systemd-tmpfiles to create Nix directories.
     system.activationScripts.nix = stringAfter [ "etc" "users" ]
       ''
         # Nix initialisation.
-        mkdir -m 0755 -p \
+        install -m 0755 -d \
           /nix/var/nix/gcroots \
           /nix/var/nix/temproots \
-          /nix/var/nix/manifests \
           /nix/var/nix/userpool \
           /nix/var/nix/profiles \
           /nix/var/nix/db \
-          /nix/var/log/nix/drvs \
-          /nix/var/nix/channel-cache
-        mkdir -m 1777 -p \
+          /nix/var/log/nix/drvs
+        install -m 1777 -d \
           /nix/var/nix/gcroots/per-user \
           /nix/var/nix/profiles/per-user \
           /nix/var/nix/gcroots/tmp
diff --git a/nixos/modules/services/misc/nix-ssh-serve.nix b/nixos/modules/services/misc/nix-ssh-serve.nix
index 66148431709f..5bd9cf9086f1 100644
--- a/nixos/modules/services/misc/nix-ssh-serve.nix
+++ b/nixos/modules/services/misc/nix-ssh-serve.nix
@@ -1,8 +1,12 @@
 { config, lib, pkgs, ... }:
 
 with lib;
-
-{
+let cfg = config.nix.sshServe;
+    command =
+      if cfg.protocol == "ssh"
+        then "nix-store --serve"
+      else "nix-daemon --stdio";
+in {
   options = {
 
     nix.sshServe = {
@@ -10,7 +14,7 @@ with lib;
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = "Whether to enable serving the Nix store as a binary cache via SSH.";
+        description = "Whether to enable serving the Nix store as a remote store via SSH.";
       };
 
       keys = mkOption {
@@ -20,14 +24,20 @@ with lib;
         description = "A list of SSH public keys allowed to access the binary cache via SSH.";
       };
 
+      protocol = mkOption {
+        type = types.enum [ "ssh" "ssh-ng" ];
+        default = "ssh";
+        description = "The specific Nix-over-SSH protocol to use.";
+      };
+
     };
 
   };
 
-  config = mkIf config.nix.sshServe.enable {
+  config = mkIf cfg.enable {
 
     users.extraUsers.nix-ssh = {
-      description = "Nix SSH substituter user";
+      description = "Nix SSH store user";
       uid = config.ids.uids.nix-ssh;
       useDefaultShell = true;
     };
@@ -41,11 +51,11 @@ with lib;
         PermitTTY no
         PermitTunnel no
         X11Forwarding no
-        ForceCommand ${config.nix.package.out}/bin/nix-store --serve
+        ForceCommand ${config.nix.package.out}/bin/${command}
       Match All
     '';
 
-    users.extraUsers.nix-ssh.openssh.authorizedKeys.keys = config.nix.sshServe.keys;
+    users.extraUsers.nix-ssh.openssh.authorizedKeys.keys = cfg.keys;
 
   };
 }
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 41cadb4a6de0..4bd1c20edf71 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -16,14 +16,14 @@ let
     It isn't perfect, but it seems to cover a vast majority of use cases.
     Caveat: even if the package is reached by a different means,
     the path above will be shown and not e.g. `${config.services.foo.package}`. */
-  manual = import ../../../doc/manual {
+  manual = import ../../../doc/manual rec {
     inherit pkgs config;
-    version = config.system.nixosRelease;
-    revision = "release-${config.system.nixosRelease}";
+    version = config.system.nixos.release;
+    revision = "release-${version}";
     options =
       let
         scrubbedEval = evalModules {
-          modules = [ { nixpkgs.system = config.nixpkgs.system; } ] ++ baseModules;
+          modules = [ { nixpkgs.localSystem = config.nixpkgs.localSystem; } ] ++ baseModules;
           args = (config._module.args) // { modules = [ ]; };
           specialArgs = { pkgs = scrubDerivations "pkgs" pkgs; };
         };
@@ -43,7 +43,7 @@ let
 
   helpScript = pkgs.writeScriptBin "nixos-help"
     ''
-      #! ${pkgs.stdenv.shell} -e
+      #! ${pkgs.runtimeShell} -e
       browser="$BROWSER"
       if [ -z "$browser" ]; then
         browser="$(type -P xdg-open || true)"
@@ -112,10 +112,10 @@ in
 
     system.build.manual = manual;
 
-    environment.systemPackages =
-      [ manual.manual helpScript ]
-      ++ optionals config.services.xserver.enable [desktopItem pkgs.nixos-icons]
-      ++ optional config.programs.man.enable manual.manpages;
+    environment.systemPackages = []
+      ++ optionals config.services.xserver.enable [ desktopItem pkgs.nixos-icons ]
+      ++ optional  config.documentation.man.enable manual.manpages
+      ++ optionals config.documentation.doc.enable [ manual.manual helpScript ];
 
     boot.extraTTYs = mkIf cfg.showManual ["tty${toString cfg.ttyNumber}"];
 
diff --git a/nixos/modules/services/misc/novacomd.nix b/nixos/modules/services/misc/novacomd.nix
new file mode 100644
index 000000000000..7cfc68d2b673
--- /dev/null
+++ b/nixos/modules/services/misc/novacomd.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.novacomd;
+
+in {
+
+  options = {
+    services.novacomd = {
+      enable = mkEnableOption "Novacom service for connecting to WebOS devices";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.webos.novacom ];
+
+    systemd.services.novacomd = {
+      description = "Novacom WebOS daemon";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.webos.novacomd}/sbin/novacomd";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ dtzWill ];
+}
diff --git a/nixos/modules/services/misc/osrm.nix b/nixos/modules/services/misc/osrm.nix
new file mode 100644
index 000000000000..7ec8b15906fc
--- /dev/null
+++ b/nixos/modules/services/misc/osrm.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.osrm;
+in
+
+{
+  options.services.osrm = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable the OSRM service.";
+    };
+
+    address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = "IP address on which the web server will listen.";
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 5000;
+      description = "Port on which the web server will run.";
+    };
+
+    threads = mkOption {
+      type = types.int;
+      default = 4;
+      description = "Number of threads to use.";
+    };
+
+    algorithm = mkOption {
+      type = types.enum [ "CH" "CoreCH" "MLD" ];
+      default = "MLD";
+      description = "Algorithm to use for the data. Must be one of CH, CoreCH, MLD";
+    };
+
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "--max-table-size 1000" "--max-matching-size 1000" ];
+      description = "Extra command line arguments passed to osrm-routed";
+    };
+
+    dataFile = mkOption {
+      type = types.path;
+      example = "/var/lib/osrm/berlin-latest.osrm";
+      description = "Data file location";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users.users.osrm = {
+      group = config.users.users.osrm.name;
+      description = "OSRM user";
+      createHome = false;
+    };
+
+    users.groups.osrm = { };
+
+    systemd.services.osrm = {
+      description = "OSRM service";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = config.users.extraUsers.osrm.name;
+        ExecStart = ''
+          ${pkgs.osrm-backend}/bin/osrm-routed \
+            --ip ${cfg.address} \
+            --port ${toString cfg.port} \
+            --threads ${toString cfg.threads} \
+            --algorithm ${cfg.algorithm} \
+            ${toString cfg.extraFlags} \
+            ${cfg.dataFile}
+        '';
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix
index ae3f84333d2d..c757093e5c1b 100644
--- a/nixos/modules/services/misc/parsoid.nix
+++ b/nixos/modules/services/misc/parsoid.nix
@@ -6,6 +6,8 @@ let
 
   cfg = config.services.parsoid;
 
+  parsoid = pkgs.nodePackages."parsoid-git://github.com/abbradar/parsoid#stable";
+
   confTree = {
     worker_heartbeat_timeout = 300000;
     logging = { level = "info"; };
@@ -93,7 +95,7 @@ in
       after = [ "network.target" ];
       serviceConfig = {
         User = "nobody";
-        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
+        ExecStart = "${parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
       };
     };
 
diff --git a/nixos/modules/services/misc/safeeyes.nix b/nixos/modules/services/misc/safeeyes.nix
new file mode 100644
index 000000000000..1a33971d9227
--- /dev/null
+++ b/nixos/modules/services/misc/safeeyes.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.safeeyes;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.safeeyes = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the safeeyes OSGi service";
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.user.services.safeeyes = {
+      description = "Safeeyes";
+
+      wantedBy = [ "graphical-session.target" ];
+      partOf   = [ "graphical-session.target" ];
+
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.safeeyes}/bin/safeeyes
+        '';
+        Restart = "on-failure";
+        RestartSec = 3;
+        StartLimitInterval = 350;
+        StartLimitBurst = 10;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/misc/serviio.nix b/nixos/modules/services/misc/serviio.nix
new file mode 100644
index 000000000000..a6612e9c6adb
--- /dev/null
+++ b/nixos/modules/services/misc/serviio.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.serviio;
+
+  serviioStart = pkgs.writeScript "serviio.sh" ''
+    #!${pkgs.bash}/bin/sh
+
+    SERVIIO_HOME=${pkgs.serviio}
+    
+    # Setup the classpath
+    SERVIIO_CLASS_PATH="$SERVIIO_HOME/lib/*:$SERVIIO_HOME/config"
+
+    # Setup Serviio specific properties
+    JAVA_OPTS="-Djava.net.preferIPv4Stack=true -Djava.awt.headless=true -Dorg.restlet.engine.loggerFacadeClass=org.restlet.ext.slf4j.Slf4jLoggerFacade
+               -Dderby.system.home=${cfg.dataDir}/library -Dserviio.home=${cfg.dataDir} -Dffmpeg.location=${pkgs.ffmpeg}/bin/ffmpeg -Ddcraw.location=${pkgs.dcraw}/bin/dcraw"
+
+    # Execute the JVM in the foreground
+    exec ${pkgs.jre}/bin/java -Xmx512M -Xms20M -XX:+UseG1GC -XX:GCTimeRatio=1 -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 $JAVA_OPTS -classpath "$SERVIIO_CLASS_PATH" org.serviio.MediaServer "$@"
+  '';
+  
+in {
+
+  ###### interface
+  options = {
+    services.serviio = {
+      
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the Serviio Media Server.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/serviio";
+        description = ''
+          The directory where serviio stores its state, data, etc.
+        '';
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.serviio = {
+      description = "Serviio Media Server";
+      after = [ "local-fs.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.serviio ];
+      serviceConfig = {
+        User = "serviio";
+        Group = "serviio";
+        ExecStart = "${serviioStart}";
+        ExecStop = "${serviioStart} -stop";
+      };
+    };
+
+    users.extraUsers = [
+      { 
+        name = "serviio";
+        group = "serviio";
+        home = cfg.dataDir;
+        description = "Serviio Media Server User";
+        createHome = true;
+        isSystemUser = true;
+      }
+    ];
+
+    users.extraGroups = [
+      { name = "serviio";} 
+    ];
+
+    networking.firewall = {
+      allowedTCPPorts = [ 
+        8895  # serve UPnP responses
+        23423 # console
+        23424 # mediabrowser
+      ];
+      allowedUDPPorts = [ 
+        1900 # UPnP service discovey
+      ];
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/ssm-agent.nix b/nixos/modules/services/misc/ssm-agent.nix
index c1e1f0903539..e951a4c7ffa8 100644
--- a/nixos/modules/services/misc/ssm-agent.nix
+++ b/nixos/modules/services/misc/ssm-agent.nix
@@ -8,11 +8,11 @@ let
   # in nixpkgs doesn't seem to work properly on NixOS, so let's just fake the two fields SSM
   # looks for. See https://github.com/aws/amazon-ssm-agent/issues/38 for upstream fix.
   fake-lsb-release = pkgs.writeScriptBin "lsb_release" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
 
     case "$1" in
       -i) echo "nixos";;
-      -r) echo "${config.system.nixosVersion}";;
+      -r) echo "${config.system.nixos.version}";;
     esac
   '';
 in {
diff --git a/nixos/modules/services/misc/xmr-stak.nix b/nixos/modules/services/misc/xmr-stak.nix
new file mode 100644
index 000000000000..57f439365471
--- /dev/null
+++ b/nixos/modules/services/misc/xmr-stak.nix
@@ -0,0 +1,73 @@
+{ lib, config, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.xmr-stak;
+
+  pkg = pkgs.xmr-stak.override {
+    inherit (cfg) openclSupport cudaSupport;
+  };
+
+  xmrConfArg = optionalString (cfg.configText != "") ("-c " +
+    pkgs.writeText "xmr-stak-config.txt" cfg.configText);
+
+in
+
+{
+  options = {
+    services.xmr-stak = {
+      enable = mkEnableOption "xmr-stak miner";
+      openclSupport = mkEnableOption "support for OpenCL (AMD/ATI graphics cards)";
+      cudaSupport = mkEnableOption "support for CUDA (NVidia graphics cards)";
+
+      extraArgs = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "--noCPU" "--currency monero" ];
+        description = "List of parameters to pass to xmr-stak.";
+      };
+
+      configText = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          "currency" : "monero",
+          "pool_list" :
+            [ { "pool_address" : "pool.supportxmr.com:5555",
+                "wallet_address" : "<long-hash>",
+                "pool_password" : "minername",
+                "pool_weight" : 1,
+              },
+            ],
+        '';
+        description = ''
+          Verbatim xmr-stak config.txt. If empty, the <literal>-c</literal>
+          parameter will not be added to the xmr-stak command.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.xmr-stak = {
+      wantedBy = [ "multi-user.target" ];
+      bindsTo = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      environment = mkIf cfg.cudaSupport {
+        LD_LIBRARY_PATH = "${pkgs.linuxPackages_latest.nvidia_x11}/lib";
+      };
+      script = ''
+        exec ${pkg}/bin/xmr-stak ${xmrConfArg} ${concatStringsSep " " cfg.extraArgs}
+      '';
+      serviceConfig = let rootRequired = cfg.openclSupport || cfg.cudaSupport; in {
+        # xmr-stak generates cpu and/or gpu configuration files
+        WorkingDirectory = "/tmp";
+        PrivateTmp = true;
+        DynamicUser = !rootRequired;
+        LimitMEMLOCK = toString (1024*1024);
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/zookeeper.nix b/nixos/modules/services/misc/zookeeper.nix
index d85b5e4ec507..91539592511c 100644
--- a/nixos/modules/services/misc/zookeeper.nix
+++ b/nixos/modules/services/misc/zookeeper.nix
@@ -106,10 +106,19 @@ in {
       '';
     };
 
+    package = mkOption {
+      description = "The zookeeper package to use";
+      default = pkgs.zookeeper;
+      defaultText = "pkgs.zookeeper";
+      type = types.package;
+    };
+
   };
 
 
   config = mkIf cfg.enable {
+    environment.systemPackages = [cfg.package];
+
     systemd.services.zookeeper = {
       description = "Zookeeper Daemon";
       wantedBy = [ "multi-user.target" ];
@@ -118,7 +127,7 @@ in {
       serviceConfig = {
         ExecStart = ''
           ${pkgs.jre}/bin/java \
-            -cp "${pkgs.zookeeper}/lib/*:${pkgs.zookeeper}/${pkgs.zookeeper.name}.jar:${configDir}" \
+            -cp "${cfg.package}/lib/*:${cfg.package}/${cfg.package.name}.jar:${configDir}" \
             ${escapeShellArgs cfg.extraCmdLineOptions} \
             -Dzookeeper.datadir.autocreate=false \
             ${optionalString cfg.preferIPv4 "-Djava.net.preferIPv4Stack=true"} \
diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix
index 9abd6e9ab641..839116de6265 100644
--- a/nixos/modules/services/monitoring/apcupsd.nix
+++ b/nixos/modules/services/monitoring/apcupsd.nix
@@ -38,7 +38,7 @@ let
   ];
 
   shellCmdsForEventScript = eventname: commands: ''
-    echo "#!${pkgs.stdenv.shell}" > "$out/${eventname}"
+    echo "#!${pkgs.runtimeShell}" > "$out/${eventname}"
     echo '${commands}' >> "$out/${eventname}"
     chmod a+x "$out/${eventname}"
   '';
diff --git a/nixos/modules/services/monitoring/fusion-inventory.nix b/nixos/modules/services/monitoring/fusion-inventory.nix
index 1c00f3c299e9..c3b869e00880 100644
--- a/nixos/modules/services/monitoring/fusion-inventory.nix
+++ b/nixos/modules/services/monitoring/fusion-inventory.nix
@@ -55,9 +55,6 @@ in {
       description = "Fusion Inventory Agent";
       wantedBy = [ "multi-user.target" ];
 
-      environment = {
-        OPTIONS = "--no-category=software";
-      };
       serviceConfig = {
         ExecStart = "${pkgs.fusionInventory}/bin/fusioninventory-agent --conf-file=${configFile} --daemon --no-fork";
       };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 4fbacef788f9..eceb91525db4 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -25,6 +25,7 @@ let
     DATABASE_USER = cfg.database.user;
     DATABASE_PASSWORD = cfg.database.password;
     DATABASE_PATH = cfg.database.path;
+    DATABASE_CONN_MAX_LIFETIME = cfg.database.connMaxLifetime;
 
     SECURITY_ADMIN_USER = cfg.security.adminUser;
     SECURITY_ADMIN_PASSWORD = cfg.security.adminPassword;
@@ -49,7 +50,7 @@ in {
     protocol = mkOption {
       description = "Which protocol to listen.";
       default = "http";
-      type = types.enum ["http" "https"];
+      type = types.enum ["http" "https" "socket"];
     };
 
     addr = mkOption {
@@ -111,7 +112,7 @@ in {
       type = mkOption {
         description = "Database type.";
         default = "sqlite3";
-        type = types.enum ["mysql" "sqlite3" "postgresql"];
+        type = types.enum ["mysql" "sqlite3" "postgres"];
       };
 
       host = mkOption {
@@ -143,6 +144,15 @@ in {
         default = "${cfg.dataDir}/data/grafana.db";
         type = types.path;
       };
+
+      connMaxLifetime = mkOption {
+        description = ''
+          Sets the maximum amount of time (in seconds) a connection may be reused.
+          For MySQL this setting should be shorter than the `wait_timeout' variable.
+        '';
+        default = 14400;
+        type = types.int;
+      };
     };
 
     security = {
@@ -241,7 +251,9 @@ in {
       description = "Grafana Service Daemon";
       wantedBy = ["multi-user.target"];
       after = ["networking.target"];
-      environment = mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
+      environment = {
+        QT_QPA_PLATFORM = "offscreen";
+      } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
       serviceConfig = {
         ExecStart = "${cfg.package.bin}/bin/grafana-server -homepath ${cfg.dataDir}";
         WorkingDirectory = cfg.dataDir;
@@ -249,7 +261,7 @@ in {
       };
       preStart = ''
         ln -fs ${cfg.package}/share/grafana/conf ${cfg.dataDir}
-        ln -fs ${cfg.package}/share/grafana/vendor ${cfg.dataDir}
+        ln -fs ${cfg.package}/share/grafana/tools ${cfg.dataDir}
       '';
     };
 
diff --git a/nixos/modules/services/monitoring/monit.nix b/nixos/modules/services/monitoring/monit.nix
index 71f50cc0f19d..d48e5c550abb 100644
--- a/nixos/modules/services/monitoring/monit.nix
+++ b/nixos/modules/services/monitoring/monit.nix
@@ -26,16 +26,10 @@ in
 
     environment.systemPackages = [ pkgs.monit ];
 
-    environment.etc = [
-      {
-        source = pkgs.writeTextFile {
-          name = "monitrc";
-          text = config.services.monit.config;
-        };
-        target = "monitrc";
-        mode = "0400";
-      }
-    ];
+    environment.etc."monitrc" = {
+      text = config.services.monit.config;
+      mode = "0400";
+    };
 
     systemd.services.monit = {
       description = "Pro-active monitoring utility for unix systems";
@@ -48,6 +42,8 @@ in
         KillMode = "process";
         Restart = "always";
       };
+      restartTriggers = [ config.environment.etc."monitrc".source ];
     };
+
   };
 }
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index cc6d51f0ef1b..358ffd431dd4 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -17,40 +17,6 @@ let
   nodeCfg = config.services.munin-node;
   cronCfg = config.services.munin-cron;
 
-  muninPlugins = pkgs.stdenv.mkDerivation {
-    name = "munin-available-plugins";
-    buildCommand = ''
-      mkdir -p $out
-
-      cp --preserve=mode ${pkgs.munin}/lib/plugins/* $out/
-
-      for file in $out/*; do
-        case "$file" in
-            */plugin.sh|*/plugins.history)
-              chmod +x "$file"
-              continue;;
-        esac
-
-        # read magic makers from the file
-        family=$(sed -nr 's/.*#%#\s+family\s*=\s*(\S+)\s*/\1/p' $file)
-        cap=$(sed -nr 's/.*#%#\s+capabilities\s*=\s*(.+)/\1/p' $file)
-
-        wrapProgram $file \
-          --set PATH "/run/wrappers/bin:/run/current-system/sw/bin" \
-          --set MUNIN_LIBDIR "${pkgs.munin}/lib" \
-          --set MUNIN_PLUGSTATE "/var/run/munin"
-
-        # munin uses markers to tell munin-node-configure what a plugin can do
-        echo "#%# family=$family" >> $file
-        echo "#%# capabilities=$cap" >> $file
-      done
-
-      # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
-      rm -f $out/diskstats
-    '';
-    buildInputs = [ pkgs.makeWrapper ];
-  };
-
   muninConf = pkgs.writeText "munin.conf"
     ''
       dbdir     /var/lib/munin
@@ -83,6 +49,29 @@ let
 
       ${nodeCfg.extraConfig}
     '';
+
+  pluginConf = pkgs.writeText "munin-plugin-conf"
+    ''
+      [hddtemp_smartctl]
+      user root
+      group root
+
+      [meminfo]
+      user root
+      group root
+
+      [ipmi*]
+      user root
+      group root
+    '';
+
+  pluginConfDir = pkgs.stdenv.mkDerivation {
+    name = "munin-plugin-conf.d";
+    buildCommand = ''
+      mkdir $out
+      ln -s ${pluginConf} $out/nixos-config
+    '';
+  };
 in
 
 {
@@ -179,17 +168,22 @@ in
       description = "Munin Node";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.munin ];
+      path = with pkgs; [ munin smartmontools "/run/current-system/sw" "/run/wrappers" ];
+      environment.MUNIN_LIBDIR = "${pkgs.munin}/lib";
       environment.MUNIN_PLUGSTATE = "/var/run/munin";
+      environment.MUNIN_LOGDIR = "/var/log/munin";
       preStart = ''
         echo "updating munin plugins..."
 
         mkdir -p /etc/munin/plugins
         rm -rf /etc/munin/plugins/*
-        PATH="/run/wrappers/bin:/run/current-system/sw/bin" ${pkgs.munin}/sbin/munin-node-configure --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${muninPlugins} --servicedir=/etc/munin/plugins 2>/dev/null | ${pkgs.bash}/bin/bash
+        ${pkgs.munin}/bin/munin-node-configure --suggest --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${pkgs.munin}/lib/plugins --servicedir=/etc/munin/plugins --sconfdir=${pluginConfDir} 2>/dev/null | ${pkgs.bash}/bin/bash
+
+        # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
+        rm /etc/munin/plugins/diskstats || true
       '';
       serviceConfig = {
-        ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/";
+        ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/ --sconfdir=${pluginConfDir}";
       };
     };
 
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index e1fde4fc9500..d23b329eeb25 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -5,18 +5,25 @@ with lib;
 let
   cfg = config.services.netdata;
 
-  configFile = pkgs.writeText "netdata.conf" cfg.configText;
+  wrappedPlugins = pkgs.runCommand "wrapped-plugins" {} ''
+    mkdir -p $out/libexec/netdata/plugins.d
+    ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin
+  '';
+
+  localConfig = {
+    global = {
+      "plugins directory" = "${wrappedPlugins}/libexec/netdata/plugins.d ${pkgs.netdata}/libexec/netdata/plugins.d";
+    };
+  };
+  mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config);
+  configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig);
 
   defaultUser = "netdata";
 
 in {
   options = {
     services.netdata = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = "Whether to enable netdata monitoring.";
-      };
+      enable = mkEnableOption "netdata";
 
       user = mkOption {
         type = types.str;
@@ -31,9 +38,9 @@ in {
       };
 
       configText = mkOption {
-        type = types.lines;
-        default = "";
-        description = "netdata.conf configuration.";
+        type = types.nullOr types.lines;
+        description = "Verbatim netdata.conf, cannot be combined with config.";
+        default = null;
         example = ''
           [global]
           debug log = syslog
@@ -42,11 +49,29 @@ in {
         '';
       };
 
+      config = mkOption {
+        type = types.attrsOf types.attrs;
+        default = {};
+        description = "netdata.conf configuration as nix attributes. cannot be combined with configText.";
+        example = literalExample ''
+          global = {
+            "debug log" = "syslog";
+            "access log" = "syslog";
+            "error log" = "syslog";
+          };
+        '';
+        };
+      };
     };
-  };
 
   config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.config != {} -> cfg.configText == null ;
+          message = "Cannot specify both config and configText";
+        }
+      ];
     systemd.services.netdata = {
+      path = with pkgs; [ gawk curl ];
       description = "Real time performance monitoring";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
@@ -66,6 +91,15 @@ in {
       };
     };
 
+    security.wrappers."apps.plugin" = {
+      source = "${pkgs.netdata}/libexec/netdata/plugins.d/apps.plugin";
+      capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
+      owner = cfg.user;
+      group = cfg.group;
+      permissions = "u+rx,g+rx,o-rwx";
+    };
+
+
     users.extraUsers = optional (cfg.user == defaultUser) {
       name = defaultUser;
     };
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
index cf761edad926..8a47c9f1e7d8 100644
--- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
@@ -111,11 +111,11 @@ in {
       after    = [ "network.target" ];
       script = ''
         ${pkgs.prometheus-alertmanager.bin}/bin/alertmanager \
-        -config.file ${alertmanagerYml} \
-        -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-        -log.level ${cfg.logLevel} \
-        ${optionalString (cfg.webExternalUrl != null) ''-web.external-url ${cfg.webExternalUrl} \''}
-        ${optionalString (cfg.logFormat != null) "-log.format ${cfg.logFormat}"}
+        --config.file ${alertmanagerYml} \
+        --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+        --log.level ${cfg.logLevel} \
+        ${optionalString (cfg.webExternalUrl != null) ''--web.external-url ${cfg.webExternalUrl} \''}
+        ${optionalString (cfg.logFormat != null) "--log.format ${cfg.logFormat}"}
       '';
 
       serviceConfig = {
diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
deleted file mode 100644
index ce2e1cf2d74b..000000000000
--- a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
+++ /dev/null
@@ -1,68 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.blackboxExporter;
-in {
-  options = {
-    services.prometheus.blackboxExporter = {
-      enable = mkEnableOption "prometheus blackbox exporter";
-
-      configFile = mkOption {
-        type = types.path;
-        description = ''
-          Path to configuration file.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 9115;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the blackbox exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-blackbox-exporter = {
-      description = "Prometheus exporter for blackbox probes";
-      unitConfig.Documentation = "https://github.com/prometheus/blackbox_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
-        ExecStart = ''
-          ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
-            --web.listen-address :${toString cfg.port} \
-            --config.file ${cfg.configFile} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix b/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix
deleted file mode 100644
index f8a5b9576a11..000000000000
--- a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix
+++ /dev/null
@@ -1,128 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.collectdExporter;
-
-  collectSettingsArgs = if (cfg.collectdBinary.enable) then ''
-    -collectd.listen-address ${optionalString (cfg.collectdBinary.listenAddress != null) cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \
-    -collectd.security-level ${cfg.collectdBinary.securityLevel} \
-  '' else "";
-
-in {
-  options = {
-    services.prometheus.collectdExporter = {
-      enable = mkEnableOption "prometheus collectd exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9103;
-        description = ''
-          Port to listen on.
-          This is used for scraping as well as the to receive collectd data via the write_http plugin.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "0.0.0.0";
-        description = ''
-          Address to listen on for web interface, telemetry and collectd JSON data.
-        '';
-      };
-
-      collectdBinary = {
-        enable = mkEnableOption "collectd binary protocol receiver";
-
-        authFile = mkOption {
-          default = null;
-          type = types.nullOr types.path;
-          description = "File mapping user names to pre-shared keys (passwords).";
-        };
-
-        port = mkOption {
-          type = types.int;
-          default = 25826;
-          description = ''Network address on which to accept collectd binary network packets.'';
-        };
-
-        listenAddress = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "0.0.0.0";
-          description = ''
-            Address to listen on for binary network packets.
-            '';
-        };
-
-        securityLevel = mkOption {
-          type = types.enum ["None" "Sign" "Encrypt"];
-          default = "None";
-          description = ''
-            Minimum required security level for accepted packets.
-            '';
-        };
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the collectd exporter.
-        '';
-      };
-
-      logFormat = mkOption {
-        type = types.str;
-        default = "logger:stderr";
-        example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true";
-        description = ''
-          Set the log target and format.
-        '';
-      };
-
-      logLevel = mkOption {
-        type = types.enum ["debug" "info" "warn" "error" "fatal"];
-        default = "info";
-        description = ''
-          Only log messages with the given severity or above.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = (optional cfg.openFirewall cfg.port) ++
-      (optional (cfg.openFirewall && cfg.collectdBinary.enable) cfg.collectdBinary.port);
-
-    systemd.services.prometheus-collectd-exporter = {
-      description = "Prometheus exporter for Collectd metrics";
-      unitConfig.Documentation = "https://github.com/prometheus/collectd_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        DynamicUser = true;
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
-            -log.format ${cfg.logFormat} \
-            -log.level ${cfg.logLevel} \
-            -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \
-            ${collectSettingsArgs} \
-            ${concatStringsSep " " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
new file mode 100644
index 000000000000..180b273d177e
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -0,0 +1,173 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters;
+
+  # each attribute in `exporterOpts` is expected to have specified:
+  #   - port        (types.int):   port on which the exporter listens
+  #   - serviceOpts (types.attrs): config that is merged with the
+  #                                default definition of the exporter's
+  #                                systemd service
+  #   - extraOpts   (types.attrs): extra configuration options to
+  #                                configure the exporter with, which
+  #                                are appended to the default options
+  #
+  #  Note that `extraOpts` is optional, but a script for the exporter's
+  #  systemd service must be provided by specifying either
+  #  `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart`
+  exporterOpts = {
+    blackbox = import ./exporters/blackbox.nix { inherit config lib pkgs; };
+    collectd = import ./exporters/collectd.nix { inherit config lib pkgs; };
+    dovecot  = import ./exporters/dovecot.nix  { inherit config lib pkgs; };
+    fritzbox = import ./exporters/fritzbox.nix { inherit config lib pkgs; };
+    json     = import ./exporters/json.nix     { inherit config lib pkgs; };
+    minio    = import ./exporters/minio.nix    { inherit config lib pkgs; };
+    nginx    = import ./exporters/nginx.nix    { inherit config lib pkgs; };
+    node     = import ./exporters/node.nix     { inherit config lib pkgs; };
+    postfix  = import ./exporters/postfix.nix  { inherit config lib pkgs; };
+    snmp     = import ./exporters/snmp.nix     { inherit config lib pkgs; };
+    unifi    = import ./exporters/unifi.nix    { inherit config lib pkgs; };
+    varnish  = import ./exporters/varnish.nix  { inherit config lib pkgs; };
+  };
+
+  mkExporterOpts = ({ name, port }: {
+    enable = mkEnableOption "the prometheus ${name} exporter";
+    port = mkOption {
+      type = types.int;
+      default = port;
+      description = ''
+        Port to listen on.
+      '';
+    };
+    listenAddress = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        Address to listen on.
+      '';
+    };
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Extra commandline options to pass to the ${name} exporter.
+      '';
+    };
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open port in firewall for incoming connections.
+      '';
+    };
+    firewallFilter = mkOption {
+      type = types.str;
+      default = "-p tcp -m tcp --dport ${toString port}";
+      example = literalExample ''
+        "-i eth0 -p tcp -m tcp --dport ${toString port}"
+      '';
+      description = ''
+        Specify a filter for iptables to use when
+        <option>services.prometheus.exporters.${name}.openFirewall</option>
+        is true. It is used as `ip46tables -I INPUT <option>firewallFilter</option> -j ACCEPT`.
+      '';
+    };
+    user = mkOption {
+      type = types.str;
+      default = "nobody";
+      description = ''
+        User name under which the ${name} exporter shall be run.
+        Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
+      '';
+    };
+    group = mkOption {
+      type = types.str;
+      default = "nobody";
+      description = ''
+        Group under which the ${name} exporter shall be run.
+        Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
+      '';
+    };
+  });
+
+  mkSubModule = { name, port, extraOpts, serviceOpts }: {
+    ${name} = mkOption {
+      type = types.submodule {
+        options = (mkExporterOpts {
+          inherit name port;
+        } // extraOpts);
+      };
+      internal = true;
+      default = {};
+    };
+  };
+
+  mkSubModules = (foldl' (a: b: a//b) {}
+    (mapAttrsToList (name: opts: mkSubModule {
+      inherit name;
+      inherit (opts) port serviceOpts;
+      extraOpts = opts.extraOpts or {};
+    }) exporterOpts)
+  );
+
+  mkExporterConf = { name, conf, serviceOpts }:
+    mkIf conf.enable {
+      networking.firewall.extraCommands = mkIf conf.openFirewall ''
+        ip46tables -I INPUT ${conf.firewallFilter} -j ACCEPT
+      '';
+      systemd.services."prometheus-${name}-exporter" = mkMerge ([{
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        serviceConfig = {
+          Restart = mkDefault "always";
+          PrivateTmp = mkDefault true;
+          WorkingDirectory = mkDefault /tmp;
+        } // mkIf (!(serviceOpts.serviceConfig.DynamicUser or false)) {
+          User = conf.user;
+          Group = conf.group;
+        };
+      } serviceOpts ]);
+  };
+in
+{
+  options.services.prometheus.exporters = mkOption {
+    type = types.submodule {
+      options = (mkSubModules);
+    };
+    description = "Prometheus exporter configuration";
+    default = {};
+    example = literalExample ''
+      {
+        node = {
+          enable = true;
+          enabledCollectors = [ "systemd" ];
+        };
+        varnish.enable = true;
+      }
+    '';
+  };
+
+  config = mkMerge ([{
+    assertions = [{
+      assertion = (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null);
+      message = ''
+        Please ensure you have either `services.prometheus.exporters.snmp.configuration'
+          or `services.prometheus.exporters.snmp.configurationPath' set!
+      '';
+    }];
+  }] ++ [(mkIf config.services.minio.enable {
+    services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
+    services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
+    services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
+  })] ++ (mapAttrsToList (name: conf:
+    mkExporterConf {
+      inherit name;
+      inherit (conf) serviceOpts;
+      conf = cfg.${name};
+    }) exporterOpts)
+  );
+
+  meta.doc = ./exporters.xml;
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.xml b/nixos/modules/services/monitoring/prometheus/exporters.xml
new file mode 100644
index 000000000000..4f0bcb298106
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters.xml
@@ -0,0 +1,135 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-services-prometheus-exporters">
+
+<title>Prometheus exporters</title>
+
+<para>Prometheus exporters provide metrics for the <link xlink:href="https://prometheus.io">prometheus monitoring system</link>.</para>
+
+<section><title>Configuration</title>
+  <para>One of the most common exporters is the <link xlink:href="https://github.com/prometheus/node_exporter">node exporter</link>, it provides hardware and OS metrics from the host it's running on. The exporter could be configured as follows:
+<programlisting>
+  services.promtheus.exporters.node = {
+    enable = true;
+    enabledCollectors = [
+      "logind"
+      "systemd"
+    ];
+    disabledCollectors = [
+      "textfile"
+    ];
+    openFirewall = true;
+    firewallFilter = "-i br0 -p tcp -m tcp --dport 9100";
+  };
+</programlisting>
+It should now serve all metrics from the collectors
+that are explicitly enabled and the ones that are
+<link xlink:href="https://github.com/prometheus/node_exporter#enabled-by-default">enabled by default</link>, via http under <literal>/metrics</literal>. In this example the firewall should just
+allow incoming connections to the exporter's port on the bridge interface <literal>br0</literal>
+(this would have to be configured seperately of course).
+For more information about configuration see <literal>man configuration.nix</literal> or
+search through the <link xlink:href="https://nixos.org/nixos/options.html#prometheus.exporters">available options</link>.
+</para>
+</section>
+<section><title>Adding a new exporter</title>
+  <para>To add a new exporter, it has to be packaged first (see <literal>nixpkgs/pkgs/servers/monitoring/prometheus/</literal> for examples), then a module can be added. The postfix exporter is used in this example:</para>
+<itemizedlist>
+  <listitem>
+    <para>
+      Some default options for all exporters are provided by
+      <literal>nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix</literal>:
+    </para>
+  </listitem>
+  <listitem override='none'>
+    <itemizedlist>
+      <listitem><para><literal>enable</literal></para></listitem>
+      <listitem><para><literal>port</literal></para></listitem>
+      <listitem><para><literal>listenAddress</literal></para></listitem>
+      <listitem><para><literal>extraFlags</literal></para></listitem>
+      <listitem><para><literal>openFirewall</literal></para></listitem>
+      <listitem><para><literal>firewallFilter</literal></para></listitem>
+      <listitem><para><literal>user</literal></para></listitem>
+      <listitem><para><literal>group</literal></para></listitem>
+    </itemizedlist>
+  </listitem>
+  <listitem>
+    <para>As there is already a package available, the module can now be added.
+      This is accomplished by adding a new file to the
+      <literal>nixos/modules/services/monitoring/prometheus/exporters/</literal> directory,
+      which will be called postfix.nix and contains all exporter specific options
+      and configuration:
+      <programlisting>
+        # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix
+        { config, lib, pkgs }:
+
+        with lib;
+
+        let
+          # for convenience we define cfg here
+          cfg = config.services.prometheus.exporters.postfix;
+        in
+        {
+          port = 9154; # The postfix exporter listens on this port by default
+
+          # `extraOpts` is an attribute set which contains additional options
+          # (and optional overrides for default options).
+          # Note that this attribute is optional.
+          extraOpts = {
+            telemetryPath = mkOption {
+              type = types.str;
+              default = "/metrics";
+              description = ''
+                Path under which to expose metrics.
+              '';
+            };
+            logfilePath = mkOption {
+              type = types.path;
+              default = /var/log/postfix_exporter_input.log;
+              example = /var/log/mail.log;
+              description = ''
+                Path where Postfix writes log entries.
+                This file will be truncated by this exporter!
+              '';
+            };
+            showqPath = mkOption {
+              type = types.path;
+              default = /var/spool/postfix/public/showq;
+              example = /var/lib/postfix/queue/public/showq;
+              description = ''
+                Path at which Postfix places its showq socket.
+              '';
+            };
+          };
+
+          # `serviceOpts` is an attribute set which contains configuration
+          # for the exporter's systemd service. One of
+          # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart`
+          # has to be specified here. This will be merged with the default
+          # service confiuration.
+          serviceOpts = {
+            serviceConfig = {
+              ExecStart = ''
+                ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
+                  --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+                  --web.telemetry-path ${cfg.telemetryPath} \
+                  ${concatStringsSep " \\\n  " cfg.extraFlags}
+              '';
+            };
+          };
+        }
+      </programlisting>
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      This should already be enough for the postfix exporter. Additionally one could
+      now add assertions and conditional default values. This can be done in the
+      'meta-module' that combines all exporter definitions and generates the submodules:
+      <literal>nixpkgs/nixos/modules/services/prometheus/exporters.nix</literal>
+    </para>
+  </listitem>
+</itemizedlist>
+</section>
+</chapter>
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
new file mode 100644
index 000000000000..d09d1c4f3663
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.blackbox;
+in
+{
+  port = 9115;
+  extraOpts = {
+    configFile = mkOption {
+      type = types.path;
+      description = ''
+        Path to configuration file.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --config.file ${cfg.configFile} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+      ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
new file mode 100644
index 000000000000..0eba3527162d
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
@@ -0,0 +1,78 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.collectd;
+in
+{
+  port = 9103;
+  extraOpts = {
+    collectdBinary = {
+      enable = mkEnableOption "collectd binary protocol receiver";
+
+      authFile = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = "File mapping user names to pre-shared keys (passwords).";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 25826;
+        description = ''Network address on which to accept collectd binary network packets.'';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          Address to listen on for binary network packets.
+          '';
+      };
+
+      securityLevel = mkOption {
+        type = types.enum ["None" "Sign" "Encrypt"];
+        default = "None";
+        description = ''
+          Minimum required security level for accepted packets.
+        '';
+      };
+    };
+
+    logFormat = mkOption {
+      type = types.str;
+      default = "logger:stderr";
+      example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true";
+      description = ''
+        Set the log target and format.
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["debug" "info" "warn" "error" "fatal"];
+      default = "info";
+      description = ''
+        Only log messages with the given severity or above.
+      '';
+    };
+  };
+  serviceOpts = let
+    collectSettingsArgs = if (cfg.collectdBinary.enable) then ''
+      -collectd.listen-address ${cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \
+      -collectd.security-level ${cfg.collectdBinary.securityLevel} \
+    '' else "";
+  in {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
+          -log.format ${cfg.logFormat} \
+          -log.level ${cfg.logLevel} \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${collectSettingsArgs} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
new file mode 100644
index 000000000000..4ca6d4e5f8b6
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.dovecot;
+in
+{
+  port = 9166;
+  extraOpts = {
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = ''
+        Path under which to expose metrics.
+      '';
+    };
+    socketPath = mkOption {
+      type = types.path;
+      default = "/var/run/dovecot/stats";
+      example = "/var/run/dovecot2/stats";
+      description = ''
+        Path under which the stats socket is placed.
+        The user/group under which the exporter runs,
+        should be able to access the socket in order
+        to scrape the metrics successfully.
+      '';
+    };
+    scopes = mkOption {
+      type = types.listOf types.str;
+      default = [ "user" ];
+      example = [ "user" "global" ];
+      description = ''
+        Stats scopes to query.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-dovecot-exporter}/bin/dovecot_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
+          --dovecot.socket-path ${cfg.socketPath} \
+          --dovecot.scopes ${concatStringsSep "," cfg.scopes} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
new file mode 100644
index 000000000000..a3f1d9d31323
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.fritzbox;
+in
+{
+  port = 9133;
+  extraOpts = {
+    gatewayAddress = mkOption {
+      type = types.str;
+      default = "fritz.box";
+      description = ''
+        The hostname or IP of the FRITZ!Box.
+      '';
+    };
+
+    gatewayPort = mkOption {
+      type = types.int;
+      default = 49000;
+      description = ''
+        The port of the FRITZ!Box UPnP service.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \
+          -listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          -gateway-address ${cfg.gatewayAddress} \
+          -gateway-port ${toString cfg.gatewayPort} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
new file mode 100644
index 000000000000..a5494e85e016
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.json;
+in
+{
+  port = 7979;
+  extraOpts = {
+    url = mkOption {
+      type = types.str;
+      description = ''
+        URL to scrape JSON from.
+      '';
+    };
+    configFile = mkOption {
+      type = types.path;
+      description = ''
+        Path to configuration file.
+      '';
+    };
+    listenAddress = {}; # not used
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
+          --port ${toString cfg.port} \
+          ${cfg.url} ${cfg.configFile} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
new file mode 100644
index 000000000000..3cc4ffdbc8fd
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
@@ -0,0 +1,65 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.minio;
+in
+{
+  port = 9290;
+  extraOpts = {
+    minioAddress = mkOption {
+      type = types.str;
+      example = "https://10.0.0.1:9000";
+      description = ''
+        The URL of the minio server.
+        Use HTTPS if Minio accepts secure connections only.
+        By default this connects to the local minio server if enabled.
+      '';
+    };
+
+    minioAccessKey = mkOption {
+      type = types.str;
+      example = "yourMinioAccessKey";
+      description = ''
+        The value of the Minio access key.
+        It is required in order to connect to the server.
+        By default this uses the one from the local minio server if enabled
+        and <literal>config.services.minio.accessKey</literal>.
+      '';
+    };
+
+    minioAccessSecret = mkOption {
+      type = types.str;
+      description = ''
+        The value of the Minio access secret.
+        It is required in order to connect to the server.
+        By default this uses the one from the local minio server if enabled
+        and <literal>config.services.minio.secretKey</literal>.
+      '';
+    };
+
+    minioBucketStats = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Collect statistics about the buckets and files in buckets.
+        It requires more computation, use it carefully in case of large buckets..
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          -minio.server ${cfg.minioAddress} \
+          -minio.access-key ${cfg.minioAccessKey} \
+          -minio.access-secret ${cfg.minioAccessSecret} \
+          ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
new file mode 100644
index 000000000000..6a3ba2d0457c
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.nginx;
+in
+{
+  port = 9113;
+  extraOpts = {
+    scrapeUri = mkOption {
+      type = types.string;
+      default = "http://localhost/nginx_status";
+      description = ''
+        Address to access the nginx status page.
+        Can be enabled with services.nginx.statusPage = true.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
+          -nginx.scrape_uri '${cfg.scrapeUri}' \
+          -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
new file mode 100644
index 000000000000..c85f5f9cfb2d
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.node;
+in
+{
+  port = 9100;
+  extraOpts = {
+    enabledCollectors = mkOption {
+      type = types.listOf types.string;
+      default = [];
+      example = ''[ "systemd" ]'';
+      description = ''
+        Collectors to enable. The collectors listed here are enabled in addition to the default ones.
+      '';
+    };
+    disabledCollectors = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = ''[ "timex" ]'';
+      description = ''
+        Collectors to disable which are enabled by default.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-node-exporter}/bin/node_exporter \
+          ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \
+          ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
new file mode 100644
index 000000000000..efe78ebcba86
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
@@ -0,0 +1,81 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.postfix;
+in
+{
+  port = 9154;
+  extraOpts = {
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = ''
+        Path under which to expose metrics.
+      '';
+    };
+    logfilePath = mkOption {
+      type = types.path;
+      default = "/var/log/postfix_exporter_input.log";
+      example = "/var/log/mail.log";
+      description = ''
+        Path where Postfix writes log entries.
+        This file will be truncated by this exporter!
+      '';
+    };
+    showqPath = mkOption {
+      type = types.path;
+      default = "/var/spool/postfix/public/showq";
+      example = "/var/lib/postfix/queue/public/showq";
+      description = ''
+        Path where Postfix places it's showq socket.
+      '';
+    };
+    systemd = {
+      enable = mkEnableOption ''
+        reading metrics from the systemd-journal instead of from a logfile
+      '';
+      unit = mkOption {
+        type = types.str;
+        default = "postfix.service";
+        description = ''
+          Name of the postfix systemd unit.
+        '';
+      };
+      slice = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Name of the postfix systemd slice.
+          This overrides the <option>systemd.unit</option>.
+        '';
+      };
+      journalPath = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to the systemd journal.
+        '';
+      };
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
+          --postfix.showq_path ${cfg.showqPath} \
+          ${concatStringsSep " \\\n  " (cfg.extraFlags
+          ++ optional cfg.systemd.enable "--systemd.enable"
+          ++ optional cfg.systemd.enable (if cfg.systemd.slice != null
+                                          then "--systemd.slice ${cfg.systemd.slice}"
+                                          else "--systemd.unit ${cfg.systemd.unit}")
+          ++ optional (cfg.systemd.enable && (cfg.systemd.journalPath != null))
+                       "--systemd.jounal_path ${cfg.systemd.journalPath}"
+          ++ optional (!cfg.systemd.enable) "--postfix.logfile_path ${cfg.logfilePath}")}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
new file mode 100644
index 000000000000..404cd0a1896b
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.snmp;
+in
+{
+  port = 9116;
+  extraOpts = {
+    configurationPath = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option.
+      '';
+      example = "./snmp.yml";
+    };
+
+    configuration = mkOption {
+      type = types.nullOr types.attrs;
+      default = {};
+      description = ''
+        Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option.
+      '';
+      example = ''
+        {
+          "default" = {
+            "version" = 2;
+            "auth" = {
+              "community" = "public";
+            };
+          };
+        };
+      '';
+    };
+
+    logFormat = mkOption {
+      type = types.str;
+      default = "logger:stderr";
+      description = ''
+        Set the log target and format.
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["debug" "info" "warn" "error" "fatal"];
+      default = "info";
+      description = ''
+        Only log messages with the given severity or above.
+      '';
+    };
+  };
+  serviceOpts = let
+    configFile = if cfg.configurationPath != null
+                 then cfg.configurationPath
+                 else "${pkgs.writeText "snmp-eporter-conf.yml" (builtins.toJSON cfg.configuration)}";
+    in {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
+          -config.file ${configFile} \
+          -log.format ${cfg.logFormat} \
+          -log.level ${cfg.logLevel} \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
new file mode 100644
index 000000000000..011dcbe208e4
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.unifi;
+in
+{
+  port = 9130;
+  extraOpts = {
+    unifiAddress = mkOption {
+      type = types.str;
+      example = "https://10.0.0.1:8443";
+      description = ''
+        URL of the UniFi Controller API.
+      '';
+    };
+
+    unifiInsecure = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        If enabled skip the verification of the TLS certificate of the UniFi Controller API.
+        Use with caution.
+      '';
+    };
+
+    unifiUsername = mkOption {
+      type = types.str;
+      example = "ReadOnlyUser";
+      description = ''
+        username for authentication against UniFi Controller API.
+      '';
+    };
+
+    unifiPassword = mkOption {
+      type = types.str;
+      description = ''
+        Password for authentication against UniFi Controller API.
+      '';
+    };
+
+    unifiTimeout = mkOption {
+      type = types.str;
+      default = "5s";
+      example = "2m";
+      description = ''
+        Timeout including unit for UniFi Controller API requests.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
+          -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \
+          -unifi.addr ${cfg.unifiAddress} \
+          -unifi.username ${cfg.unifiUsername} \
+          -unifi.password ${cfg.unifiPassword} \
+          -unifi.timeout ${cfg.unifiTimeout} \
+          ${optionalString cfg.unifiInsecure "-unifi.insecure" } \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
new file mode 100644
index 000000000000..b439a83e7aa2
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.varnish;
+in
+{
+  port = 9131;
+  serviceOpts = {
+    path = [ pkgs.varnish ];
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
deleted file mode 100644
index 6da39b6519cb..000000000000
--- a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
+++ /dev/null
@@ -1,76 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.fritzboxExporter;
-in {
-  options = {
-    services.prometheus.fritzboxExporter = {
-      enable = mkEnableOption "prometheus fritzbox exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9133;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      gatewayAddress = mkOption {
-        type = types.str;
-        default = "fritz.box";
-        description = ''
-          The hostname or IP of the FRITZ!Box.
-        '';
-      };
-
-      gatewayPort = mkOption {
-        type = types.int;
-        default = 49000;
-        description = ''
-          The port of the FRITZ!Box UPnP service.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the fritzbox exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-fritzbox-exporter = {
-      description = "Prometheus exporter for FRITZ!Box via UPnP";
-      unitConfig.Documentation = "https://github.com/ndecker/fritzbox_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \
-            -listen-address :${toString cfg.port} \
-            -gateway-address ${cfg.gatewayAddress} \
-            -gateway-port ${toString cfg.gatewayPort} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/json-exporter.nix b/nixos/modules/services/monitoring/prometheus/json-exporter.nix
deleted file mode 100644
index 6bc56df9834b..000000000000
--- a/nixos/modules/services/monitoring/prometheus/json-exporter.nix
+++ /dev/null
@@ -1,74 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.jsonExporter;
-in {
-  options = {
-    services.prometheus.jsonExporter = {
-      enable = mkEnableOption "prometheus JSON exporter";
-
-      url = mkOption {
-        type = types.str;
-        description = ''
-          URL to scrape JSON from.
-        '';
-      };
-
-      configFile = mkOption {
-        type = types.path;
-        description = ''
-          Path to configuration file.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 7979;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the JSON exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-json-exporter = {
-      description = "Prometheus exporter for JSON over HTTP";
-      unitConfig.Documentation = "https://github.com/kawamuray/prometheus-json-exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
-            --port ${toString cfg.port} \
-            ${cfg.url} ${cfg.configFile} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix b/nixos/modules/services/monitoring/prometheus/minio-exporter.nix
deleted file mode 100644
index 4314671523cf..000000000000
--- a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix
+++ /dev/null
@@ -1,117 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.minioExporter;
-in {
-  options = {
-    services.prometheus.minioExporter = {
-      enable = mkEnableOption "prometheus minio exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9290;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "0.0.0.0";
-        description = ''
-          Address to listen on for web interface and telemetry.
-        '';
-      };
-
-      minioAddress = mkOption {
-        type = types.str;
-        example = "https://10.0.0.1:9000";
-        default = if config.services.minio.enable then "http://localhost:9000" else null;
-        description = ''
-          The URL of the minio server.
-          Use HTTPS if Minio accepts secure connections only.
-          By default this connects to the local minio server if enabled.
-        '';
-      };
-
-      minioAccessKey = mkOption ({
-        type = types.str;
-        example = "BKIKJAA5BMMU2RHO6IBB";
-        description = ''
-          The value of the Minio access key.
-          It is required in order to connect to the server.
-          By default this uses the one from the local minio server if enabled
-          and <literal>config.services.minio.accessKey</literal>.
-        '';
-      } // optionalAttrs (config.services.minio.enable && config.services.minio.accessKey != "") {
-        default = config.services.minio.accessKey;
-      });
-
-      minioAccessSecret = mkOption ({
-        type = types.str;
-        description = ''
-          The calue of the Minio access secret.
-          It is required in order to connect to the server.
-          By default this uses the one from the local minio server if enabled
-          and <literal>config.services.minio.secretKey</literal>.
-        '';
-      } // optionalAttrs (config.services.minio.enable && config.services.minio.secretKey != "") {
-        default = config.services.minio.secretKey;
-      });
-
-      minioBucketStats = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Collect statistics about the buckets and files in buckets.
-          It requires more computation, use it carefully in case of large buckets..
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the minio exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-minio-exporter = {
-      description = "Prometheus exporter for Minio server metrics";
-      unitConfig.Documentation = "https://github.com/joe-pll/minio-exporter";
-      wantedBy = [ "multi-user.target" ];
-      after = optional config.services.minio.enable "minio.service";
-      serviceConfig = {
-        DynamicUser = true;
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
-            -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \
-            -minio.server ${cfg.minioAddress} \
-            -minio.access-key ${cfg.minioAccessKey} \
-            -minio.access-secret ${cfg.minioAccessSecret} \
-            ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
deleted file mode 100644
index 1ccafee3b18b..000000000000
--- a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.nginxExporter;
-in {
-  options = {
-    services.prometheus.nginxExporter = {
-      enable = mkEnableOption "prometheus nginx exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9113;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.string;
-        default = "0.0.0.0";
-        description = ''
-          Address to listen on.
-        '';
-      };
-
-      scrapeUri = mkOption {
-        type = types.string;
-        default = "http://localhost/nginx_status";
-        description = ''
-          Address to access the nginx status page.
-          Can be enabled with services.nginx.statusPage = true.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the nginx exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-nginx-exporter = {
-      after = [ "network.target" "nginx.service" ];
-      description = "Prometheus exporter for nginx metrics";
-      unitConfig.Documentation = "https://github.com/discordianfish/nginx_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart  = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
-            -nginx.scrape_uri '${cfg.scrapeUri}' \
-            -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/node-exporter.nix b/nixos/modules/services/monitoring/prometheus/node-exporter.nix
deleted file mode 100644
index bad4389ce799..000000000000
--- a/nixos/modules/services/monitoring/prometheus/node-exporter.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.nodeExporter;
-in {
-  options = {
-    services.prometheus.nodeExporter = {
-      enable = mkEnableOption "prometheus node exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9100;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.string;
-        default = "0.0.0.0";
-        description = ''
-          Address to listen on.
-        '';
-      };
-
-      enabledCollectors = mkOption {
-        type = types.listOf types.string;
-        default = [];
-        example = ''[ "systemd" ]'';
-        description = ''
-          Collectors to enable. The collectors listed here are enabled in addition to the default ones.
-        '';
-      };
-
-      disabledCollectors = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = ''[ "timex" ]'';
-        description = ''
-          Collectors to disable which are enabled by default.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the node exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-node-exporter = {
-      description = "Prometheus exporter for machine metrics";
-      unitConfig.Documentation = "https://github.com/prometheus/node_exporter";
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        exec ${pkgs.prometheus-node-exporter}/bin/node_exporter \
-          ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \
-          ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \
-          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-          ${concatStringsSep " \\\n  " cfg.extraFlags}
-      '';
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
deleted file mode 100644
index fe33f8c1f04d..000000000000
--- a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
+++ /dev/null
@@ -1,127 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.snmpExporter;
-  mkConfigFile = pkgs.writeText "snmp.yml" (if cfg.configurationPath == null then builtins.toJSON cfg.configuration else builtins.readFile cfg.configurationPath);
-in {
-  options = {
-    services.prometheus.snmpExporter = {
-      enable = mkEnableOption "Prometheus snmp exporter";
-
-      user = mkOption {
-        type = types.str;
-        default = "nobody";
-        description = ''
-          User name under which snmp exporter shall be run.
-        '';
-      };
-
-      group = mkOption {
-        type = types.str;
-        default = "nogroup";
-        description = ''
-          Group under which snmp exporter shall be run.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 9116;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Address to listen on for web interface and telemetry.
-        '';
-      };
-
-      configurationPath = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        description = ''
-          Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option.
-        '';
-        example = "./snmp.yml";
-      };
-
-      configuration = mkOption {
-        type = types.nullOr types.attrs;
-        default = {};
-        description = ''
-          Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option.
-        '';
-        example = ''
-          {
-            "default" = {
-              "version" = 2;
-              "auth" = {
-                "community" = "public";
-              };
-            };
-          };
-        '';
-      };
-
-      logFormat = mkOption {
-        type = types.str;
-        default = "logger:stderr";
-        description = ''
-          Set the log target and format.
-        '';
-      };
-
-      logLevel = mkOption {
-        type = types.enum ["debug" "info" "warn" "error" "fatal"];
-        default = "info";
-        description = ''
-          Only log messages with the given severity or above.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    assertions = singleton
-      {
-        assertion = (cfg.configurationPath == null) != (cfg.configuration == null);
-        message = "Please ensure you have either 'configuration' or 'configurationPath' set!";
-      };
-
-    systemd.services.prometheus-snmp-exporter = {
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      script = ''
-        ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
-          -config.file ${mkConfigFile} \
-          -log.format ${cfg.logFormat} \
-          -log.level ${cfg.logLevel} \
-          -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port}
-      '';
-
-      serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
-        Restart  = "always";
-        PrivateTmp = true;
-        WorkingDirectory = "/tmp";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
deleted file mode 100644
index 0a56d6ae95a5..000000000000
--- a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.unifiExporter;
-in {
-  options = {
-    services.prometheus.unifiExporter = {
-      enable = mkEnableOption "prometheus unifi exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9130;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      unifiAddress = mkOption {
-        type = types.str;
-        example = "https://10.0.0.1:8443";
-        description = ''
-          URL of the UniFi Controller API.
-        '';
-      };
-
-      unifiInsecure = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          If enabled skip the verification of the TLS certificate of the UniFi Controller API.
-          Use with caution.
-        '';
-      };
-      
-      unifiUsername = mkOption {
-        type = types.str;
-        example = "ReadOnlyUser";
-        description = ''
-          username for authentication against UniFi Controller API.
-        '';
-      };
-      
-      unifiPassword = mkOption {
-        type = types.str;
-        description = ''
-          Password for authentication against UniFi Controller API.
-        '';
-      };
-      
-      unifiTimeout = mkOption {
-        type = types.str;
-        default = "5s";
-        example = "2m";
-        description = ''
-          Timeout including unit for UniFi Controller API requests.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the unifi exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-unifi-exporter = {
-      description = "Prometheus exporter for UniFi Controller metrics";
-      unitConfig.Documentation = "https://github.com/mdlayher/unifi_exporter";
-      wantedBy = [ "multi-user.target" ];
-      after = optional config.services.unifi.enable "unifi.service";
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
-            -telemetry.addr :${toString cfg.port} \
-            -unifi.addr ${cfg.unifiAddress} \
-            -unifi.username ${cfg.unifiUsername} \
-            -unifi.password ${cfg.unifiPassword} \
-            -unifi.timeout ${cfg.unifiTimeout} \
-            ${optionalString cfg.unifiInsecure "-unifi.insecure" } \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
deleted file mode 100644
index 143ebb62aeac..000000000000
--- a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
+++ /dev/null
@@ -1,61 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-# Shamelessly cribbed from nginx-exporter.nix. ~ C.
-with lib;
-
-let
-  cfg = config.services.prometheus.varnishExporter;
-in {
-  options = {
-    services.prometheus.varnishExporter = {
-      enable = mkEnableOption "prometheus Varnish exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9131;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the Varnish exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-varnish-exporter = {
-      description = "Prometheus exporter for Varnish metrics";
-      unitConfig.Documentation = "https://github.com/jonnenauha/prometheus_varnish_exporter";
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.varnish ];
-      script = ''
-        exec ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
-          -web.listen-address :${toString cfg.port} \
-          ${concatStringsSep " \\\n  " cfg.extraFlags}
-      '';
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index 1d6940c516a9..fecae4ca1b36 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -14,7 +14,7 @@ let
   nx = cfg.notifications.x11;
 
   smartdNotify = pkgs.writeScript "smartd-notify.sh" ''
-    #! ${pkgs.stdenv.shell}
+    #! ${pkgs.runtimeShell}
     ${optionalString nm.enable ''
       {
       ${pkgs.coreutils}/bin/cat << EOF
@@ -64,7 +64,7 @@ let
        "DEVICESCAN ${notifyOpts}${cfg.defaults.autodetected}"}
   '';
 
-  smartdOpts = { name, ... }: {
+  smartdDeviceOpts = { name, ... }: {
 
     options = {
 
@@ -108,6 +108,18 @@ in
         '';
       };
 
+      extraOptions = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        example = ["-A /var/log/smartd/" "--interval=3600"];
+        description = ''
+          Extra command-line options passed to the <literal>smartd</literal>
+          daemon on startup.
+
+          (See <literal>man 8 smartd</literal>.)
+        '';
+      };
+
       notifications = {
 
         mail = {
@@ -197,7 +209,7 @@ in
       devices = mkOption {
         default = [];
         example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
-        type = with types; listOf (submodule smartdOpts);
+        type = with types; listOf (submodule smartdDeviceOpts);
         description = "List of devices to monitor.";
       };
 
@@ -222,7 +234,7 @@ in
 
       path = [ pkgs.nettools ]; # for hostname and dnsdomanname calls in smartd
 
-      serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd --no-fork --configfile=${smartdConf}";
+      serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd ${lib.concatStringsSep " " cfg.extraOptions} --no-fork --configfile=${smartdConf}";
     };
 
   };
diff --git a/nixos/modules/services/monitoring/statsd.nix b/nixos/modules/services/monitoring/statsd.nix
index df2adb9f2766..7b0e9981cbb1 100644
--- a/nixos/modules/services/monitoring/statsd.nix
+++ b/nixos/modules/services/monitoring/statsd.nix
@@ -9,6 +9,12 @@ let
   isBuiltinBackend = name:
     builtins.elem name [ "graphite" "console" "repeater" ];
 
+  backendsToPackages = let
+    mkMap = list: name:
+      if isBuiltinBackend name then list
+      else list ++ [ pkgs.nodePackages.${name} ];
+  in foldl mkMap [];
+
   configFile = pkgs.writeText "statsd.conf" ''
     {
       address: "${cfg.listenAddress}",
@@ -27,13 +33,21 @@ let
         prettyprint: false
       },
       log: {
-        backend: "syslog"
+        backend: "stdout"
       },
       automaticConfigReload: false${optionalString (cfg.extraConfig != null) ","}
       ${cfg.extraConfig}
     }
   '';
 
+  deps = pkgs.buildEnv {
+    name = "statsd-runtime-deps";
+    pathsToLink = [ "/lib" ];
+    ignoreCollisions = true;
+
+    paths = backendsToPackages cfg.backends;
+  };
+
 in
 
 {
@@ -42,11 +56,7 @@ in
 
   options.services.statsd = {
 
-    enable = mkOption {
-      description = "Whether to enable statsd stats aggregation service";
-      default = false;
-      type = types.bool;
-    };
+    enable = mkEnableOption "statsd";
 
     listenAddress = mkOption {
       description = "Address that statsd listens on over UDP";
@@ -110,6 +120,11 @@ in
 
   config = mkIf cfg.enable {
 
+    assertions = map (backend: {
+      assertion = !isBuiltinBackend backend -> hasAttrByPath [ backend ] pkgs.nodePackages;
+      message = "Only builtin backends (graphite, console, repeater) or backends enumerated in `pkgs.nodePackages` are allowed!";
+    }) cfg.backends;
+
     users.extraUsers = singleton {
       name = "statsd";
       uid = config.ids.uids.statsd;
@@ -120,9 +135,7 @@ in
       description = "Statsd Server";
       wantedBy = [ "multi-user.target" ];
       environment = {
-        NODE_PATH=concatMapStringsSep ":"
-          (pkg: "${builtins.getAttr pkg pkgs.statsd.nodePackages}/lib/node_modules")
-          (filter (name: !isBuiltinBackend name) cfg.backends);
+        NODE_PATH = "${deps}/lib/node_modules";
       };
       serviceConfig = {
         ExecStart = "${pkgs.statsd}/bin/statsd ${configFile}";
diff --git a/nixos/modules/services/network-filesystems/beegfs.nix b/nixos/modules/services/network-filesystems/beegfs.nix
new file mode 100644
index 000000000000..a6a2ec6cbc36
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/beegfs.nix
@@ -0,0 +1,343 @@
+{ config, lib, pkgs, ...} :
+
+with lib;
+
+let
+  cfg = config.services.beegfs;
+
+  # functions for the generations of config files
+
+  configMgmtd = name: cfg: pkgs.writeText "mgmt-${name}.conf" ''
+    storeMgmtdDirectory = ${cfg.mgmtd.storeDir}
+    storeAllowFirstRunInit = false
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+
+    ${cfg.mgmtd.extraConfig}
+  '';
+
+  configAdmon = name: cfg: pkgs.writeText "admon-${name}.conf" ''
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+
+    ${cfg.admon.extraConfig}
+  '';
+
+  configMeta = name: cfg: pkgs.writeText "meta-${name}.conf" ''
+    storeMetaDirectory = ${cfg.meta.storeDir}
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+    storeAllowFirstRunInit = false
+
+    ${cfg.mgmtd.extraConfig}
+  '';
+
+  configStorage = name: cfg: pkgs.writeText "storage-${name}.conf" ''
+    storeStorageDirectory = ${cfg.storage.storeDir}
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+    storeAllowFirstRunInit = false
+
+    ${cfg.storage.extraConfig}
+  '';
+
+  configHelperd = name: cfg: pkgs.writeText "helperd-${name}.conf" ''
+    connAuthFile = ${cfg.connAuthFile}
+    ${cfg.helperd.extraConfig}
+  '';
+
+  configClientFilename = name : "/etc/beegfs/client-${name}.conf";
+
+  configClient = name: cfg: ''
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+
+    ${cfg.client.extraConfig}
+  '';
+
+  serviceList = [
+    { service = "admon"; cfgFile = configAdmon; }
+    { service = "meta"; cfgFile = configMeta; }
+    { service = "mgmtd"; cfgFile = configMgmtd; }
+    { service = "storage"; cfgFile = configStorage; }
+  ];
+
+  # functions to generate systemd.service entries
+
+  systemdEntry = service: cfgFile: (mapAttrs' ( name: cfg:
+    (nameValuePair "beegfs-${service}-${name}" (mkIf cfg."${service}".enable {
+    wantedBy = [ "multi-user.target" ];
+    requires = [ "network-online.target" ];
+    after = [ "network-online.target" ];
+    serviceConfig = rec {
+      ExecStart = ''
+        ${pkgs.beegfs}/bin/beegfs-${service} \
+          cfgFile=${cfgFile name cfg} \
+          pidFile=${PIDFile}
+      '';
+      PIDFile = "/run/beegfs-${service}-${name}.pid";
+      TimeoutStopSec = "300";
+    };
+  }))) cfg);
+
+  systemdHelperd =  mapAttrs' ( name: cfg:
+    (nameValuePair "beegfs-helperd-${name}" (mkIf cfg.client.enable {
+    wantedBy = [ "multi-user.target" ];
+    requires = [ "network-online.target" ];
+    after = [ "network-online.target" ];
+    serviceConfig = rec {
+      ExecStart = ''
+        ${pkgs.beegfs}/bin/beegfs-helperd \
+          cfgFile=${configHelperd name cfg} \
+          pidFile=${PIDFile}
+      '';
+      PIDFile = "/run/beegfs-helperd-${name}.pid";
+      TimeoutStopSec = "300";
+    };
+   }))) cfg;
+
+  # wrappers to beegfs tools. Avoid typing path of config files
+  utilWrappers = mapAttrsToList ( name: cfg:
+      ( pkgs.runCommand "beegfs-utils-${name}" { nativeBuildInputs = [ pkgs.makeWrapper ]; } ''
+        mkdir -p $out/bin
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-check-servers \
+                    $out/bin/beegfs-check-servers-${name} \
+                    --add-flags "-c ${configClientFilename name}" \
+                    --prefix PATH : ${lib.makeBinPath [ pkgs.beegfs ]}
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-ctl \
+                    $out/bin/beegfs-ctl-${name} \
+                    --add-flags "--cfgFile=${configClientFilename name}"
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-ctl \
+                    $out/bin/beegfs-df-${name} \
+                    --add-flags "--cfgFile=${configClientFilename name}" \
+                    --add-flags --listtargets  \
+                    --add-flags --hidenodeid \
+                    --add-flags --pools \
+                    --add-flags --spaceinfo
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-fsck \
+                    $out/bin/beegfs-fsck-${name} \
+                    --add-flags "--cfgFile=${configClientFilename name}"
+      ''
+     )) cfg;
+in
+{
+  ###### interface
+
+  options = {
+    services.beegfsEnable = mkEnableOption "BeeGFS";
+
+    services.beegfs = mkOption {
+      default = {};
+      description = ''
+        BeeGFS configurations. Every mount point requires a separate configuration.
+      '';
+      type = with types; attrsOf (submodule ({ config, ... } : {
+        options = {
+          mgmtdHost = mkOption {
+            type = types.str;
+            default = null;
+            example = "master";
+            description = ''Hostname of managament host.'';
+          };
+
+          connAuthFile = mkOption {
+            type = types.str;
+            default = "";
+            example = "/etc/my.key";
+            description = "File containing shared secret authentication.";
+          };
+
+          connPortShift = mkOption {
+            type = types.int;
+            default = 0;
+            example = 5;
+            description = ''
+              For each additional beegfs configuration shift all
+              service TCP/UDP ports by at least 5.
+            '';
+          };
+
+          client = {
+            enable = mkEnableOption "BeeGFS client";
+
+            mount = mkOption {
+              type = types.bool;
+              default = true;
+              description = "Create fstab entry automatically";
+            };
+
+            mountPoint = mkOption {
+              type = types.str;
+              default = "/run/beegfs";
+              description = ''
+                Mount point under which the beegfs filesytem should be mounted.
+                If mounted manually the mount option specifing the config file is needed:
+                cfgFile=/etc/beegfs/beegfs-client-&lt;name&gt;.conf
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-client.conf.
+                See documentation for further details.
+             '';
+            };
+          };
+
+          helperd = {
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-helperd.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          mgmtd = {
+            enable = mkEnableOption "BeeGFS mgmtd daemon";
+
+            storeDir = mkOption {
+              type = types.path;
+              default = null;
+              example = "/data/beegfs-mgmtd";
+              description = ''
+                Data directory for mgmtd.
+                Must not be shared with other beegfs daemons.
+                This directory must exist and it must be initialized
+                with beegfs-setup-mgmtd, e.g. "beegfs-setup-mgmtd -C -p &lt;storeDir&gt;"
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-mgmtd.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          admon = {
+            enable = mkEnableOption "BeeGFS admon daemon";
+
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-admon.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          meta = {
+            enable = mkEnableOption "BeeGFS meta data daemon";
+
+            storeDir = mkOption {
+              type = types.path;
+              default = null;
+              example = "/data/beegfs-meta";
+              description = ''
+                Data directory for meta data service.
+                Must not be shared with other beegfs daemons.
+                The underlying filesystem must be mounted with xattr turned on.
+                This directory must exist and it must be initialized
+                with beegfs-setup-meta, e.g.
+                "beegfs-setup-meta -C -s &lt;serviceID&gt; -p &lt;storeDir&gt;"
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                Additional lines for beegfs-meta.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          storage = {
+            enable = mkEnableOption "BeeGFS storage daemon";
+
+            storeDir = mkOption {
+              type = types.path;
+              default = null;
+              example = "/data/beegfs-storage";
+              description = ''
+                Data directories for storage service.
+                Must not be shared with other beegfs daemons.
+                The underlying filesystem must be mounted with xattr turned on.
+                This directory must exist and it must be initialized
+                with beegfs-setup-storage, e.g.
+                "beegfs-setup-storage -C -s &lt;serviceID&gt; -i &lt;storageTargetID&gt; -p &lt;storeDir&gt;"
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                Addional lines for beegfs-storage.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+        };
+      }));
+    };
+  };
+
+  ###### implementation
+
+  config =
+    mkIf config.services.beegfsEnable {
+
+    environment.systemPackages = utilWrappers;
+
+    # Put the client.conf files in /etc since they are needed
+    # by the commandline tools
+    environment.etc = mapAttrs' ( name: cfg:
+      (nameValuePair "beegfs/client-${name}.conf" (mkIf (cfg.client.enable)
+    {
+      enable = true;
+      text = configClient name cfg;
+    }))) cfg;
+
+    # Kernel module, we need it only once per host.
+    boot = mkIf (
+      foldr (a: b: a || b) false
+        (map (x: x.client.enable) (collect (x: x ? client) cfg)))
+    {
+      kernelModules = [ "beegfs" ];
+      extraModulePackages = [ pkgs.linuxPackages.beegfs-module ];
+    };
+
+    # generate fstab entries
+    fileSystems = mapAttrs' (name: cfg:
+      (nameValuePair cfg.client.mountPoint (optionalAttrs cfg.client.mount (mkIf cfg.client.enable {
+      device = "beegfs_nodev";
+      fsType = "beegfs";
+      mountPoint = cfg.client.mountPoint;
+      options = [ "cfgFile=${configClientFilename name}" "_netdev" ];
+    })))) cfg;
+
+    # generate systemd services
+    systemd.services = systemdHelperd //
+      foldr (a: b: a // b) {}
+        (map (x: systemdEntry x.service x.cfgFile) serviceList);
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/ceph.nix b/nixos/modules/services/network-filesystems/ceph.nix
new file mode 100644
index 000000000000..5de8ae79a246
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/ceph.nix
@@ -0,0 +1,371 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  ceph = pkgs.ceph;
+  cfg  = config.services.ceph;
+  # function that translates "camelCaseOptions" to "camel case options", credits to tilpner in #nixos@freenode
+  translateOption = replaceStrings upperChars (map (s: " ${s}") lowerChars);
+  generateDaemonList = (daemonType: daemons: extraServiceConfig:
+    mkMerge (
+      map (daemon: 
+        { "ceph-${daemonType}-${daemon}" = generateServiceFile daemonType daemon cfg.global.clusterName ceph extraServiceConfig; }
+      ) daemons
+    )
+  );
+  generateServiceFile = (daemonType: daemonId: clusterName: ceph: extraServiceConfig: {
+    enable = true;
+    description = "Ceph ${builtins.replaceStrings lowerChars upperChars daemonType} daemon ${daemonId}";
+    after = [ "network-online.target" "local-fs.target" "time-sync.target" ] ++ optional (daemonType == "osd") "ceph-mon.target";
+    wants = [ "network-online.target" "local-fs.target" "time-sync.target" ];
+    partOf = [ "ceph-${daemonType}.target" ];
+    wantedBy = [ "ceph-${daemonType}.target" ];
+
+    serviceConfig = {
+      LimitNOFILE = 1048576;
+      LimitNPROC = 1048576;
+      Environment = "CLUSTER=${clusterName}";
+      ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      PrivateDevices = "yes";
+      PrivateTmp = "true";
+      ProtectHome = "true";
+      ProtectSystem = "full";
+      Restart = "on-failure";
+      StartLimitBurst = "5";
+      StartLimitInterval = "30min";
+      ExecStart = "${ceph.out}/bin/${if daemonType == "rgw" then "radosgw" else "ceph-${daemonType}"} -f --cluster ${clusterName} --id ${if daemonType == "rgw" then "client.${daemonId}" else daemonId} --setuser ceph --setgroup ceph";
+    } // extraServiceConfig
+      // optionalAttrs (daemonType == "osd") { ExecStartPre = "${ceph.out}/libexec/ceph/ceph-osd-prestart.sh --id ${daemonId} --cluster ${clusterName}"; };
+    } // optionalAttrs (builtins.elem daemonType [ "mds" "mon" "rgw" "mgr" ]) { preStart = ''
+        daemonPath="/var/lib/ceph/${if daemonType == "rgw" then "radosgw" else daemonType}/${clusterName}-${daemonId}"
+        if [ ! -d ''$daemonPath ]; then
+          mkdir -m 755 -p ''$daemonPath
+          chown -R ceph:ceph ''$daemonPath 
+        fi
+      '';
+    } // optionalAttrs (daemonType == "osd") { path = [ pkgs.getopt ]; }
+  );
+  generateTargetFile = (daemonType:
+    {
+      "ceph-${daemonType}" = {
+        description = "Ceph target allowing to start/stop all ceph-${daemonType} services at once";
+        partOf = [ "ceph.target" ];
+        before = [ "ceph.target" ];
+      };
+    }
+  );
+in 
+{
+  options.services.ceph = {
+    # Ceph has a monolithic configuration file but different sections for
+    # each daemon, a separate client section and a global section
+    enable = mkEnableOption "Ceph global configuration";
+
+    global = {
+      fsid = mkOption {
+        type = types.str;
+        example = ''
+          433a2193-4f8a-47a0-95d2-209d7ca2cca5
+        '';
+        description = ''
+          Filesystem ID, a generated uuid, its must be generated and set before
+          attempting to start a cluster
+        '';
+      };
+
+      clusterName = mkOption {
+        type = types.str;
+        default = "ceph";
+        description = ''
+          Name of cluster
+        '';
+      };
+
+      monInitialMembers = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          node0, node1, node2 
+        '';
+        description = ''
+          List of hosts that will be used as monitors at startup.
+        '';
+      };
+
+      monHost = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          10.10.0.1, 10.10.0.2, 10.10.0.3
+        '';
+        description = ''
+          List of hostname shortnames/IP addresses of the initial monitors.
+        '';
+      };
+
+      maxOpenFiles = mkOption {
+        type = types.int;
+        default = 131072;
+        description = ''
+          Max open files for each OSD daemon.
+        '';
+      };
+
+      authClusterRequired = mkOption {
+        type = types.enum [ "cephx" "none" ];
+        default = "cephx";
+        description = ''
+          Enables requiring daemons to authenticate with eachother in the cluster.
+        '';
+      };
+
+      authServiceRequired = mkOption {
+        type = types.enum [ "cephx" "none" ];
+        default = "cephx";
+        description = ''
+          Enables requiring clients to authenticate with the cluster to access services in the cluster (e.g. radosgw, mds or osd).
+        '';
+      };
+
+      authClientRequired = mkOption {
+        type = types.enum [ "cephx" "none" ];
+        default = "cephx";
+        description = ''
+          Enables requiring the cluster to authenticate itself to the client.
+        '';
+      };
+
+      publicNetwork = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          10.20.0.0/24, 192.168.1.0/24
+        '';
+        description = ''
+          A comma-separated list of subnets that will be used as public networks in the cluster.
+        '';
+      };
+
+      clusterNetwork = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          10.10.0.0/24, 192.168.0.0/24
+        '';
+        description = ''
+          A comma-separated list of subnets that will be used as cluster networks in the cluster.
+        '';
+      };
+    };
+
+    mgr = {
+      enable = mkEnableOption "Ceph MGR daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of names for manager daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in mgr.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = ''
+          Extra configuration to add to the global section for manager daemons.
+        '';
+      };
+    };
+
+    mon = {
+      enable = mkEnableOption "Ceph MON daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of monitor daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in mon.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = ''
+          Extra configuration to add to the monitor section.
+        '';
+      };
+    };
+
+    osd = {
+      enable = mkEnableOption "Ceph OSD daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of OSD daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in osd.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {
+          "osd journal size" = "10000";
+          "osd pool default size" = "3";
+          "osd pool default min size" = "2";
+          "osd pool default pg num" = "200";
+          "osd pool default pgp num" = "200";
+          "osd crush chooseleaf type" = "1";
+        };
+        description = ''
+          Extra configuration to add to the OSD section.
+        '';
+      };
+    };
+
+    mds = {
+      enable = mkEnableOption "Ceph MDS daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of metadata service daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in mds.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = ''
+          Extra configuration to add to the MDS section.
+        '';
+      };
+    };
+
+    rgw = {
+      enable = mkEnableOption "Ceph RadosGW daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of rados gateway daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in client.name1, radosgw daemons
+          aren't daemons to cluster in the sense that OSD, MGR or MON daemons are. They are simply
+          daemons, from ceph, that uses the cluster as a backend.
+        '';
+      };
+    };
+
+    client = {
+      enable = mkEnableOption "Ceph client configuration";
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        example = ''
+          {
+            # This would create a section for a radosgw daemon named node0 and related
+            # configuration for it
+            "client.radosgw.node0" = { "some config option" = "true"; };
+          };
+        '';
+        description = ''
+          Extra configuration to add to the client section. Configuration for rados gateways
+          would be added here, with their own sections, see example.
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.ceph.enable {
+    assertions = [
+      { assertion = cfg.global.fsid != "";
+        message = "fsid has to be set to a valid uuid for the cluster to function";
+      }
+      { assertion = cfg.mgr.enable == true;
+        message = "ceph 12.x requires atleast 1 MGR daemon enabled for the cluster to function";
+      }
+      { assertion = cfg.mon.enable == true -> cfg.mon.daemons != [];
+        message = "have to set id of atleast one MON if you're going to enable Monitor";
+      }
+      { assertion = cfg.mds.enable == true -> cfg.mds.daemons != [];
+        message = "have to set id of atleast one MDS if you're going to enable Metadata Service";
+      }
+      { assertion = cfg.osd.enable == true -> cfg.osd.daemons != [];
+        message = "have to set id of atleast one OSD if you're going to enable OSD";
+      }
+      { assertion = cfg.mgr.enable == true -> cfg.mgr.daemons != [];
+        message = "have to set id of atleast one MGR if you're going to enable MGR";
+      }
+    ];
+
+    warnings = optional (cfg.global.monInitialMembers == null) 
+      ''Not setting up a list of members in monInitialMembers requires that you set the host variable for each mon daemon or else the cluster won't function'';
+    
+    environment.etc."ceph/ceph.conf".text = let
+      # Translate camelCaseOptions to the expected camel case option for ceph.conf
+      translatedGlobalConfig = mapAttrs' (name: value: nameValuePair (translateOption name) value) cfg.global;
+      # Merge the extraConfig set for mgr daemons, as mgr don't have their own section
+      globalAndMgrConfig = translatedGlobalConfig // optionalAttrs cfg.mgr.enable cfg.mgr.extraConfig;
+      # Remove all name-value pairs with null values from the attribute set to avoid making empty sections in the ceph.conf
+      globalConfig = mapAttrs' (name: value: nameValuePair (translateOption name) value) (filterAttrs (name: value: value != null) globalAndMgrConfig);
+      totalConfig = {
+          "global" = globalConfig;
+        } // optionalAttrs (cfg.mon.enable && cfg.mon.extraConfig != {}) { "mon" = cfg.mon.extraConfig; }
+          // optionalAttrs (cfg.mds.enable && cfg.mds.extraConfig != {}) { "mds" = cfg.mds.extraConfig; }
+          // optionalAttrs (cfg.osd.enable && cfg.osd.extraConfig != {}) { "osd" = cfg.osd.extraConfig; }
+          // optionalAttrs (cfg.client.enable && cfg.client.extraConfig != {})  cfg.client.extraConfig;
+      in
+        generators.toINI {} totalConfig;
+
+    users.extraUsers = singleton {
+      name = "ceph";
+      uid = config.ids.uids.ceph;
+      description = "Ceph daemon user";
+    };
+
+    users.extraGroups = singleton {
+      name = "ceph";
+      gid = config.ids.gids.ceph;
+    };
+
+    systemd.services = let
+      services = [] 
+        ++ optional cfg.mon.enable (generateDaemonList "mon" cfg.mon.daemons { RestartSec = "10"; }) 
+        ++ optional cfg.mds.enable (generateDaemonList "mds" cfg.mds.daemons { StartLimitBurst = "3"; })
+        ++ optional cfg.osd.enable (generateDaemonList "osd" cfg.osd.daemons { StartLimitBurst = "30"; RestartSec = "20s"; })
+        ++ optional cfg.rgw.enable (generateDaemonList "rgw" cfg.rgw.daemons { })
+        ++ optional cfg.mgr.enable (generateDaemonList "mgr" cfg.mgr.daemons { StartLimitBurst = "3"; });
+      in 
+        mkMerge services;
+
+    systemd.targets = let
+      targets = [
+        { "ceph" = { description = "Ceph target allowing to start/stop all ceph service instances at once"; }; }
+      ] ++ optional cfg.mon.enable (generateTargetFile "mon")
+        ++ optional cfg.mds.enable (generateTargetFile "mds")
+        ++ optional cfg.osd.enable (generateTargetFile "osd")
+        ++ optional cfg.rgw.enable (generateTargetFile "rgw")
+        ++ optional cfg.mgr.enable (generateTargetFile "mgr");
+      in
+        mkMerge targets;
+
+    systemd.tmpfiles.rules = [
+      "d /run/ceph 0770 ceph ceph -"
+    ];
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/davfs2.nix b/nixos/modules/services/network-filesystems/davfs2.nix
new file mode 100644
index 000000000000..6b2a770100c5
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/davfs2.nix
@@ -0,0 +1,74 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.davfs2;
+  cfgFile = pkgs.writeText "davfs2.conf" ''
+    dav_user ${cfg.davUser}
+    dav_group ${cfg.davGroup}
+    ${cfg.extraConfig}
+  '';
+in
+{
+  options.services.davfs2 = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable davfs2.
+      '';
+    };
+
+    davUser = mkOption {
+      type = types.string;
+      default = "davfs2";
+      description = ''
+        When invoked by root the mount.davfs daemon will run as this user.
+        Value must be given as name, not as numerical id.
+      '';
+    };
+
+    davGroup = mkOption {
+      type = types.string;
+      default = "davfs2";
+      description = ''
+        The group of the running mount.davfs daemon. Ordinary users must be
+        member of this group in order to mount a davfs2 file system. Value must
+        be given as name, not as numerical id.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        kernel_fs coda
+        proxy foo.bar:8080
+        use_locks 0
+      '';
+      description = ''
+        Extra lines appended to the configuration of davfs2.
+      ''  ;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.davfs2 ];
+    environment.etc."davfs2/davfs2.conf".source = cfgFile;
+
+    users.extraGroups = optionalAttrs (cfg.davGroup == "davfs2") (singleton {
+      name = "davfs2";
+      gid = config.ids.gids.davfs2;
+    });
+
+    users.extraUsers = optionalAttrs (cfg.davUser == "davfs2") (singleton {
+      name = "davfs2";
+      createHome = false;
+      group = cfg.davGroup;
+      uid = config.ids.uids.davfs2;
+      description = "davfs2 user";
+    });
+  };
+
+}
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index d4a695ef5880..39a4fd6beff8 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -39,8 +39,6 @@ let
     # NB: migration must be performed prior to pre-start, else we get the failure message!
     preStart = ''
       ipfs repo fsck # workaround for BUG #4212 (https://github.com/ipfs/go-ipfs/issues/4214)
-      ipfs --local config Addresses.API ${cfg.apiAddress}
-      ipfs --local config Addresses.Gateway ${cfg.gatewayAddress}
     '' + optionalString cfg.autoMount ''
       ipfs --local config Mounts.FuseAllowOther --json true
       ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir}
@@ -56,7 +54,11 @@ let
               EOF
               ipfs --local config --json "${concatStringsSep "." path}" "$value"
             '')
-            cfg.extraConfig)
+            ({ Addresses.API = cfg.apiAddress;
+               Addresses.Gateway = cfg.gatewayAddress;
+               Addresses.Swarm = cfg.swarmAddress;
+            } //
+            cfg.extraConfig))
         );
     serviceConfig = {
       ExecStart = "${wrapped}/bin/ipfs daemon ${ipfsFlags}";
@@ -140,6 +142,12 @@ in {
         description = "Where IPFS exposes its API to";
       };
 
+      swarmAddress = mkOption {
+        type = types.listOf types.str;
+        default = [ "/ip4/0.0.0.0/tcp/4001" "/ip6/::/tcp/4001" ];
+        description = "Where IPFS listens for incoming p2p connections";
+      };
+
       enableGC = mkOption {
         type = types.bool;
         default = false;
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
deleted file mode 100644
index 0946e379e796..000000000000
--- a/nixos/modules/services/network-filesystems/openafs-client/default.nix
+++ /dev/null
@@ -1,99 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-let
-  inherit (lib) mkOption mkIf;
-
-  cfg = config.services.openafsClient;
-
-  cellServDB = pkgs.fetchurl {
-    url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14;
-    sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc";
-  };
-
-  afsConfig = pkgs.runCommand "afsconfig" {} ''
-    mkdir -p $out
-    echo ${cfg.cellName} > $out/ThisCell
-    cp ${cellServDB} $out/CellServDB
-    echo "/afs:${cfg.cacheDirectory}:${cfg.cacheSize}" > $out/cacheinfo
-  '';
-
-  openafsPkgs = config.boot.kernelPackages.openafsClient;
-in
-{
-  ###### interface
-
-  options = {
-
-    services.openafsClient = {
-
-      enable = mkOption {
-        default = false;
-        description = "Whether to enable the OpenAFS client.";
-      };
-
-      cellName = mkOption {
-        default = "grand.central.org";
-        description = "Cell name.";
-      };
-
-      cacheSize = mkOption {
-        default = "100000";
-        description = "Cache size.";
-      };
-
-      cacheDirectory = mkOption {
-        default = "/var/cache/openafs";
-        description = "Cache directory.";
-      };
-
-      crypt = mkOption {
-        default = false;
-        description = "Whether to enable (weak) protocol encryption.";
-      };
-
-      sparse = mkOption {
-        default = false;
-        description = "Minimal cell list in /afs.";
-      };
-
-    };
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    environment.systemPackages = [ openafsPkgs ];
-
-    environment.etc = [
-      { source = afsConfig;
-        target = "openafs";
-      }
-    ];
-
-    systemd.services.afsd = {
-      description = "AFS client";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      serviceConfig = { RemainAfterExit = true; };
-
-      preStart = ''
-        mkdir -p -m 0755 /afs
-        mkdir -m 0700 -p ${cfg.cacheDirectory}
-        ${pkgs.kmod}/bin/insmod ${openafsPkgs}/lib/openafs/libafs-*.ko || true
-        ${openafsPkgs}/sbin/afsd -confdir ${afsConfig} -cachedir ${cfg.cacheDirectory} ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} -fakestat -afsdb
-        ${openafsPkgs}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
-      '';
-
-      # Doing this in preStop, because after these commands AFS is basically
-      # stopped, so systemd has nothing to do, just noticing it.  If done in
-      # postStop, then we get a hang + kernel oops, because AFS can't be
-      # stopped simply by sending signals to processes.
-      preStop = ''
-        ${pkgs.utillinux}/bin/umount /afs
-        ${openafsPkgs}/sbin/afsd -shutdown
-      '';
-    };
-  };
-}
diff --git a/nixos/modules/services/network-filesystems/openafs/client.nix b/nixos/modules/services/network-filesystems/openafs/client.nix
new file mode 100644
index 000000000000..3826fe3edfd0
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/client.nix
@@ -0,0 +1,239 @@
+{ config, pkgs, lib, ... }:
+
+with import ./lib.nix { inherit lib; };
+
+let
+  inherit (lib) getBin mkOption mkIf optionalString singleton types;
+
+  cfg = config.services.openafsClient;
+
+  cellServDB = pkgs.fetchurl {
+    url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14;
+    sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc";
+  };
+
+  clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.cellServDB);
+
+  afsConfig = pkgs.runCommand "afsconfig" {} ''
+    mkdir -p $out
+    echo ${cfg.cellName} > $out/ThisCell
+    cat ${cellServDB} ${clientServDB} > $out/CellServDB
+    echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo
+  '';
+
+  openafsMod = config.boot.kernelPackages.openafs;
+  openafsBin = lib.getBin pkgs.openafs;
+in
+{
+  ###### interface
+
+  options = {
+
+    services.openafsClient = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the OpenAFS client.";
+      };
+
+      afsdb = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Resolve cells via AFSDB DNS records.";
+      };
+
+      cellName = mkOption {
+        default = "";
+        type = types.str;
+        description = "Cell name.";
+        example = "grand.central.org";
+      };
+
+      cellServDB = mkOption {
+        default = [];
+        type = with types; listOf (submodule { options = cellServDBConfig; });
+        description = ''
+          This cell's database server records, added to the global
+          CellServDB. See CellServDB(5) man page for syntax. Ignored when
+          <literal>afsdb</literal> is set to <literal>true</literal>.
+        '';
+        example = ''
+          [ { ip = "1.2.3.4"; dnsname = "first.afsdb.server.dns.fqdn.org"; }
+            { ip = "2.3.4.5"; dnsname = "second.afsdb.server.dns.fqdn.org"; }
+          ]
+        '';
+      };
+
+      cache = {
+        blocks = mkOption {
+          default = 100000;
+          type = types.int;
+          description = "Cache size in 1KB blocks.";
+        };
+
+        chunksize = mkOption {
+          default = 0;
+          type = types.ints.between 0 30;
+          description = ''
+            Size of each cache chunk given in powers of
+            2. <literal>0</literal> resets the chunk size to its default
+            values (13 (8 KB) for memcache, 18-20 (256 KB to 1 MB) for
+            diskcache). Maximum value is 30. Important performance
+            parameter. Set to higher values when dealing with large files.
+          '';
+        };
+
+        directory = mkOption {
+          default = "/var/cache/openafs";
+          type = types.str;
+          description = "Cache directory.";
+        };
+
+        diskless = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            Use in-memory cache for diskless machines. Has no real
+            performance benefit anymore.
+          '';
+        };
+      };
+
+      crypt = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Whether to enable (weak) protocol encryption.";
+      };
+
+      daemons = mkOption {
+        default = 2;
+        type = types.int;
+        description = ''
+          Number of daemons to serve user requests. Numbers higher than 6
+          usually do no increase performance. Default is sufficient for up
+          to five concurrent users.
+        '';
+      };
+
+      fakestat = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Return fake data on stat() calls. If <literal>true</literal>,
+          always do so. If <literal>false</literal>, only do so for
+          cross-cell mounts (as these are potentially expensive).
+        '';
+      };
+
+      inumcalc = mkOption {
+        default = "compat";
+        type = types.strMatching "compat|md5";
+        description = ''
+          Inode calculation method. <literal>compat</literal> is
+          computationally less expensive, but <literal>md5</literal> greatly
+          reduces the likelihood of inode collisions in larger scenarios
+          involving multiple cells mounted into one AFS space.
+        '';
+      };
+
+      mountPoint = mkOption {
+        default = "/afs";
+        type = types.str;
+        description = ''
+          Mountpoint of the AFS file tree, conventionally
+          <literal>/afs</literal>. When set to a different value, only
+          cross-cells that use the same value can be accessed.
+        '';
+      };
+
+      sparse = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Minimal cell list in /afs.";
+      };
+
+      startDisconnected = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Start up in disconnected mode.  You need to execute
+          <literal>fs disco online</literal> (as root) to switch to
+          connected mode. Useful for roaming devices.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.afsdb || cfg.cellServDB != [];
+        message = "You should specify all cell-local database servers in config.services.openafsClient.cellServDB or set config.services.openafsClient.afsdb.";
+      }
+      { assertion = cfg.cellName != "";
+        message = "You must specify the local cell name in config.services.openafsClient.cellName.";
+      }
+    ];
+
+    environment.systemPackages = [ pkgs.openafs ];
+
+    environment.etc = {
+      clientCellServDB = {
+        source = pkgs.runCommand "CellServDB" {} ''
+          cat ${cellServDB} ${clientServDB} > $out
+        '';
+        target = "openafs/CellServDB";
+        mode = "0644";
+      };
+      clientCell = {
+        text = ''
+          ${cfg.cellName}
+        '';
+        target = "openafs/ThisCell";
+        mode = "0644";
+      };
+    };
+
+    systemd.services.afsd = {
+      description = "AFS client";
+      wantedBy = [ "multi-user.target" ];
+      after = singleton (if cfg.startDisconnected then  "network.target" else "network-online.target");
+      serviceConfig = { RemainAfterExit = true; };
+      restartIfChanged = false;
+
+      preStart = ''
+        mkdir -p -m 0755 ${cfg.mountPoint}
+        mkdir -m 0700 -p ${cfg.cache.directory}
+        ${pkgs.kmod}/bin/insmod ${openafsMod}/lib/modules/*/extra/openafs/libafs.ko.xz
+        ${openafsBin}/sbin/afsd \
+          -mountdir ${cfg.mountPoint} \
+          -confdir ${afsConfig} \
+          ${optionalString (!cfg.cache.diskless) "-cachedir ${cfg.cache.directory}"} \
+          -blocks ${toString cfg.cache.blocks} \
+          -chunksize ${toString cfg.cache.chunksize} \
+          ${optionalString cfg.cache.diskless "-memcache"} \
+          -inumcalc ${cfg.inumcalc} \
+          ${if cfg.fakestat then "-fakestat-all" else "-fakestat"} \
+          ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} \
+          ${optionalString cfg.afsdb "-afsdb"}
+        ${openafsBin}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
+        ${optionalString cfg.startDisconnected "${openafsBin}/bin/fs discon offline"}
+      '';
+
+      # Doing this in preStop, because after these commands AFS is basically
+      # stopped, so systemd has nothing to do, just noticing it.  If done in
+      # postStop, then we get a hang + kernel oops, because AFS can't be
+      # stopped simply by sending signals to processes.
+      preStop = ''
+        ${pkgs.utillinux}/bin/umount ${cfg.mountPoint}
+        ${openafsBin}/sbin/afsd -shutdown
+        ${pkgs.kmod}/sbin/rmmod libafs
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/openafs/lib.nix b/nixos/modules/services/network-filesystems/openafs/lib.nix
new file mode 100644
index 000000000000..ecfc72d2eaf9
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/lib.nix
@@ -0,0 +1,28 @@
+{ lib, ...}:
+
+let
+  inherit (lib) concatStringsSep mkOption types;
+
+in rec {
+
+  mkCellServDB = cellName: db: ''
+    >${cellName}
+  '' + (concatStringsSep "\n" (map (dbm: if (dbm.ip != "" && dbm.dnsname != "") then dbm.ip + " #" + dbm.dnsname else "")
+                                   db));
+
+  # CellServDB configuration type
+  cellServDBConfig = {
+    ip = mkOption {
+      type = types.str;
+      default = "";
+      example = "1.2.3.4";
+      description = "IP Address of a database server";
+    };
+    dnsname = mkOption {
+      type = types.str;
+      default = "";
+      example = "afs.example.org";
+      description = "DNS full-qualified domain name of a database server";
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/openafs/server.nix b/nixos/modules/services/network-filesystems/openafs/server.nix
new file mode 100644
index 000000000000..429eb945ac9e
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/server.nix
@@ -0,0 +1,260 @@
+{ config, pkgs, lib, ... }:
+
+with import ./lib.nix { inherit lib; };
+
+let
+  inherit (lib) concatStringsSep intersperse mapAttrsToList mkForce mkIf mkMerge mkOption optionalString types;
+
+  bosConfig = pkgs.writeText "BosConfig" (''
+    restrictmode 1
+    restarttime 16 0 0 0 0
+    checkbintime 3 0 5 0 0
+  '' + (optionalString cfg.roles.database.enable ''
+    bnode simple vlserver 1
+    parm ${openafsBin}/libexec/openafs/vlserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.vlserverArgs}
+    end
+    bnode simple ptserver 1
+    parm ${openafsBin}/libexec/openafs/ptserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.ptserverArgs}
+    end
+  '') + (optionalString cfg.roles.fileserver.enable ''
+    bnode dafs dafs 1
+    parm ${openafsBin}/libexec/openafs/dafileserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.fileserverArgs}
+    parm ${openafsBin}/libexec/openafs/davolserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.volserverArgs}
+    parm ${openafsBin}/libexec/openafs/salvageserver ${cfg.roles.fileserver.salvageserverArgs}
+    parm ${openafsBin}/libexec/openafs/dasalvager ${cfg.roles.fileserver.salvagerArgs}
+    end
+  '') + (optionalString (cfg.roles.database.enable && cfg.roles.backup.enable) ''
+    bnode simple buserver 1
+    parm ${openafsBin}/libexec/openafs/buserver ${cfg.roles.backup.buserverArgs} ${optionalString (cfg.roles.backup.cellServDB != []) "-cellservdb /etc/openafs/backup/"}
+    end
+  ''));
+
+  netInfo = if (cfg.advertisedAddresses != []) then
+    pkgs.writeText "NetInfo" ((concatStringsSep "\nf " cfg.advertisedAddresses) + "\n")
+  else null;
+
+  buCellServDB = pkgs.writeText "backup-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.roles.backup.cellServDB);
+
+  cfg = config.services.openafsServer;
+
+  udpSizeStr = toString cfg.udpPacketSize;
+
+  openafsBin = lib.getBin pkgs.openafs;
+
+in {
+
+  options = {
+
+    services.openafsServer = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable the OpenAFS server. An OpenAFS server needs a
+          complex setup. So, be aware that enabling this service and setting
+          some options does not give you a turn-key-ready solution. You need
+          at least a running Kerberos 5 setup, as OpenAFS relies on it for
+          authentication. See the Guide "QuickStartUnix" coming with
+          <literal>pkgs.openafs.doc</literal> for complete setup
+          instructions.
+        '';
+      };
+
+      advertisedAddresses = mkOption {
+        default = [];
+        description = "List of IP addresses this server is advertised under. See NetInfo(5)";
+      };
+
+      cellName = mkOption {
+        default = "";
+        type = types.str;
+        description = "Cell name, this server will serve.";
+        example = "grand.central.org";
+      };
+
+      cellServDB = mkOption {
+        default = [];
+        type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
+        description = "Definition of all cell-local database server machines.";
+      };
+
+      roles = {
+        fileserver = {
+          enable = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Fileserver role, serves files and volumes from its local storage.";
+          };
+
+          fileserverArgs = mkOption {
+            default = "-vattachpar 128 -vhashsize 11 -L -rxpck 400 -cb 1000000";
+            type = types.str;
+            description = "Arguments to the dafileserver process. See its man page.";
+          };
+
+          volserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the davolserver process. See its man page.";
+            example = "-sync never";
+          };
+
+          salvageserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the salvageserver process. See its man page.";
+            example = "-showlog";
+          };
+
+          salvagerArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the dasalvager process. See its man page.";
+            example = "-showlog -showmounts";
+          };
+        };
+
+        database = {
+          enable = mkOption {
+            default = true;
+            type = types.bool;
+            description = ''
+              Database server role, maintains the Volume Location Database,
+              Protection Database (and Backup Database, see
+              <literal>backup</literal> role). There can be multiple
+              servers in the database role for replication, which then need
+              reliable network connection to each other.
+
+              Servers in this role appear in AFSDB DNS records or the
+              CellServDB.
+            '';
+          };
+
+          vlserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the vlserver process. See its man page.";
+            example = "-rxbind";
+          };
+
+          ptserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the ptserver process. See its man page.";
+            example = "-restricted -default_access S---- S-M---";
+          };
+        };
+
+        backup = {
+          enable = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Backup server role. Use in conjunction with the
+              <literal>database</literal> role to maintain the Backup
+              Database. Normally only used in conjunction with tape storage
+              or IBM's Tivoli Storage Manager.
+            '';
+          };
+
+          buserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the buserver process. See its man page.";
+            example = "-p 8";
+          };
+
+          cellServDB = mkOption {
+            default = [];
+            type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
+            description = ''
+              Definition of all cell-local backup database server machines.
+              Use this when your cell uses less backup database servers than
+              other database server machines.
+            '';
+          };
+        };
+      };
+
+      dottedPrincipals= mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled, allow principal names containing (.) dots. Enabling
+          this has security implications!
+        '';
+      };
+
+      udpPacketSize = mkOption {
+        default = 1310720;
+        type = types.int;
+        description = ''
+          UDP packet size to use in Bytes. Higher values can speed up
+          communications. The default of 1 MB is a sufficient in most
+          cases. Make sure to increase the kernel's UDP buffer size
+          accordingly via <literal>net.core(w|r|opt)mem_max</literal>
+          sysctl.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.cellServDB != [];
+        message = "You must specify all cell-local database servers in config.services.openafsServer.cellServDB.";
+      }
+      { assertion = cfg.cellName != "";
+        message = "You must specify the local cell name in config.services.openafsServer.cellName.";
+      }
+    ];
+
+    environment.systemPackages = [ pkgs.openafs ];
+
+    environment.etc = {
+      bosConfig = {
+        source = bosConfig;
+        target = "openafs/BosConfig";
+        mode = "0644";
+      };
+      cellServDB = {
+        text = mkCellServDB cfg.cellName cfg.cellServDB;
+        target = "openafs/server/CellServDB";
+        mode = "0644";
+      };
+      thisCell = {
+        text = cfg.cellName;
+        target = "openafs/server/ThisCell";
+        mode = "0644";
+      };
+      buCellServDB = {
+        enable = (cfg.roles.backup.cellServDB != []);
+        text = mkCellServDB cfg.cellName cfg.roles.backup.cellServDB;
+        target = "openafs/backup/CellServDB";
+      };
+    };
+
+    systemd.services = {
+      openafs-server = {
+        description = "OpenAFS server";
+        after = [ "syslog.target" "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        restartIfChanged = false;
+        unitConfig.ConditionPathExists = [ "/etc/openafs/server/rxkad.keytab" ];
+        preStart = ''
+          mkdir -m 0755 -p /var/openafs
+          ${optionalString (netInfo != null) "cp ${netInfo} /var/openafs/netInfo"}
+          ${optionalString (cfg.roles.backup.cellServDB != []) "cp ${buCellServDB}"}
+        '';
+        serviceConfig = {
+          ExecStart = "${openafsBin}/bin/bosserver -nofork";
+          ExecStop = "${openafsBin}/bin/bos shutdown localhost -wait -localauth";
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 9b9c91a4f167..b23266e8d43a 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -54,9 +54,12 @@ let
       };
 
       serviceConfig = {
-        ExecStart = "${samba}/sbin/${appName} ${args}";
+        ExecStart = "${samba}/sbin/${appName} --foreground --no-process-group ${args}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        LimitNOFILE = 16384;
+        PIDFile = "/run/${appName}.pid";
         Type = "notify";
+        NotifyAccess = "all"; #may not do anything...
       };
 
       restartTriggers = [ configFile ];
@@ -230,11 +233,12 @@ in
             after = [ "samba-setup.service" "network.target" ];
             wantedBy = [ "multi-user.target" ];
           };
-
+          # Refer to https://github.com/samba-team/samba/tree/master/packaging/systemd
+          # for correct use with systemd
           services = {
-            "samba-smbd" = daemonService "smbd" "-F";
-            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "-F");
-            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "-F");
+            "samba-smbd" = daemonService "smbd" "";
+            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "");
+            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "");
             "samba-setup" = {
               description = "Samba Setup Task";
               script = setupScript;
diff --git a/nixos/modules/services/network-filesystems/xtreemfs.nix b/nixos/modules/services/network-filesystems/xtreemfs.nix
index 0c6714563d8a..95d7641e8b53 100644
--- a/nixos/modules/services/network-filesystems/xtreemfs.nix
+++ b/nixos/modules/services/network-filesystems/xtreemfs.nix
@@ -11,7 +11,7 @@ let
   home = cfg.homeDir;
 
   startupScript = class: configPath: pkgs.writeScript "xtreemfs-osd.sh" ''
-    #! ${pkgs.stdenv.shell}
+    #! ${pkgs.runtimeShell}
     JAVA_HOME="${pkgs.jdk}"
     JAVADIR="${xtreemfs}/share/java"
     JAVA_CALL="$JAVA_HOME/bin/java -ea -cp $JAVADIR/XtreemFS.jar:$JAVADIR/BabuDB.jar:$JAVADIR/Flease.jar:$JAVADIR/protobuf-java-2.5.0.jar:$JAVADIR/Foundation.jar:$JAVADIR/jdmkrt.jar:$JAVADIR/jdmktk.jar:$JAVADIR/commons-codec-1.3.jar"
diff --git a/nixos/modules/services/network-filesystems/yandex-disk.nix b/nixos/modules/services/network-filesystems/yandex-disk.nix
index 4de206641331..44b0edf62018 100644
--- a/nixos/modules/services/network-filesystems/yandex-disk.nix
+++ b/nixos/modules/services/network-filesystems/yandex-disk.nix
@@ -99,10 +99,10 @@ in
             exit 1
         fi
 
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \
           -c '${pkgs.yandex-disk}/bin/yandex-disk token -p ${cfg.password} ${cfg.username} ${dir}/token'
 
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \
           -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory} --exclude-dirs=${cfg.excludes}'
       '';
 
diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix
index fc7d56a24fa7..9898f164c5cf 100644
--- a/nixos/modules/services/networking/amuled.nix
+++ b/nixos/modules/services/networking/amuled.nix
@@ -68,7 +68,7 @@ in
       '';
 
       script = ''
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${user} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${user} \
             -c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled'
       '';
     };
diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix
index ad4ac9bf45e3..df9c92db2e54 100644
--- a/nixos/modules/services/networking/aria2.nix
+++ b/nixos/modules/services/networking/aria2.nix
@@ -10,9 +10,9 @@ let
   settingsDir = "${homeDir}";
   sessionFile = "${homeDir}/aria2.session";
   downloadDir = "${homeDir}/Downloads";
-  
+
   rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to);
-  
+
   settingsFile = pkgs.writeText "aria2.conf"
   ''
     dir=${cfg.downloadDir}
@@ -110,12 +110,12 @@ in
         mkdir -m 0770 -p "${homeDir}"
         chown aria2:aria2 "${homeDir}"
         if [[ ! -d "${config.services.aria2.downloadDir}" ]]
-        then 
+        then
           mkdir -m 0770 -p "${config.services.aria2.downloadDir}"
           chown aria2:aria2 "${config.services.aria2.downloadDir}"
         fi
         if [[ ! -e "${sessionFile}" ]]
-        then 
+        then
           touch "${sessionFile}"
           chown aria2:aria2 "${sessionFile}"
         fi
@@ -132,4 +132,4 @@ in
       };
     };
   };
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix
index 1a7a1e24b702..c25bd0fdc541 100644
--- a/nixos/modules/services/networking/bird.nix
+++ b/nixos/modules/services/networking/bird.nix
@@ -7,21 +7,27 @@ let
     let
       cfg = config.services.${variant};
       pkg = pkgs.${variant};
+      birdBin = if variant == "bird6" then "bird6" else "bird";
       birdc = if variant == "bird6" then "birdc6" else "birdc";
+      descr =
+        { bird = "1.9.x with IPv4 suport";
+          bird6 = "1.9.x with IPv6 suport";
+          bird2 = "2.x";
+        }.${variant};
       configFile = pkgs.stdenv.mkDerivation {
         name = "${variant}.conf";
         text = cfg.config;
         preferLocalBuild = true;
         buildCommand = ''
           echo -n "$text" > $out
-          ${pkg}/bin/${variant} -d -p -c $out
+          ${pkg}/bin/${birdBin} -d -p -c $out
         '';
       };
     in {
       ###### interface
       options = {
         services.${variant} = {
-          enable = mkEnableOption "BIRD Internet Routing Daemon";
+          enable = mkEnableOption "BIRD Internet Routing Daemon (${descr})";
           config = mkOption {
             type = types.lines;
             description = ''
@@ -36,12 +42,12 @@ let
       config = mkIf cfg.enable {
         environment.systemPackages = [ pkg ];
         systemd.services.${variant} = {
-          description = "BIRD Internet Routing Daemon";
+          description = "BIRD Internet Routing Daemon (${descr})";
           wantedBy = [ "multi-user.target" ];
           serviceConfig = {
             Type = "forking";
             Restart = "on-failure";
-            ExecStart = "${pkg}/bin/${variant} -c ${configFile} -u ${variant} -g ${variant}";
+            ExecStart = "${pkg}/bin/${birdBin} -c ${configFile} -u ${variant} -g ${variant}";
             ExecReload = "${pkg}/bin/${birdc} configure";
             ExecStop = "${pkg}/bin/${birdc} down";
             CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_SETUID" "CAP_SETGID"
@@ -56,14 +62,15 @@ let
         users = {
           extraUsers.${variant} = {
             description = "BIRD Internet Routing Daemon user";
-            group = "${variant}";
+            group = variant;
           };
           extraGroups.${variant} = {};
         };
       };
     };
 
-  inherit (config.services) bird bird6;
-in {
-  imports = [(generic "bird") (generic "bird6")];
+in
+
+{
+  imports = map generic [ "bird" "bird6" "bird2" ];
 }
diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix
index 546d27069232..c3ca6fbe725e 100644
--- a/nixos/modules/services/networking/connman.nix
+++ b/nixos/modules/services/networking/connman.nix
@@ -52,6 +52,15 @@ in {
         '';
       };
 
+      extraFlags = mkOption {
+        type = with types; listOf string;
+        default = [ ];
+        example = [ "--nodnsproxy" ];
+        description = ''
+          Extra flags to pass to connmand
+        '';
+      };
+
     };
 
   };
@@ -81,7 +90,7 @@ in {
         Type = "dbus";
         BusName = "net.connman";
         Restart = "on-failure";
-        ExecStart = "${pkgs.connman}/sbin/connmand --config=${configFile} --nodaemon";
+        ExecStart = "${pkgs.connman}/sbin/connmand --config=${configFile} --nodaemon ${toString cfg.extraFlags}";
         StandardOutput = "null";
       };
     };
diff --git a/nixos/modules/services/networking/dante.nix b/nixos/modules/services/networking/dante.nix
index a9a77f3412af..32acce51e692 100644
--- a/nixos/modules/services/networking/dante.nix
+++ b/nixos/modules/services/networking/dante.nix
@@ -47,7 +47,7 @@ in
 
     systemd.services.dante = {
       description   = "Dante SOCKS v4 and v5 compatible proxy server";
-      after         = [ "network.target" ];
+      after         = [ "network-online.target" ];
       wantedBy      = [ "multi-user.target" ];
 
       serviceConfig = {
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index 9e56545f746c..9a2e13e9553c 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -3,24 +3,24 @@
 let
   cfg = config.services.ddclient;
   boolToStr = bool: if bool then "yes" else "no";
+  dataDir = "/var/lib/ddclient";
 
   configText = ''
     # This file can be used as a template for configFile or is automatically generated by Nix options.
-    daemon=${toString cfg.interval}
-    cache=${cfg.homeDir}/ddclient.cache
-    pid=/run/ddclient/ddclient.pid
-    foreground=NO
+    cache=${dataDir}/ddclient.cache
+    foreground=YES
     use=${cfg.use}
     login=${cfg.username}
     password=${cfg.password}
     protocol=${cfg.protocol}
-    ${let server = cfg.server; in
-      lib.optionalString (server != "") "server=${server}"}
+    ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
+    ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
+    ${lib.optionalString (cfg.zone != "")   "zone=${cfg.zone}"}
     ssl=${boolToStr cfg.ssl}
     wildcard=YES
     quiet=${boolToStr cfg.quiet}
     verbose=${boolToStr cfg.verbose}
-    ${cfg.domain}
+    ${lib.concatStringsSep "," cfg.domains}
     ${cfg.extraConfig}
   '';
 
@@ -44,17 +44,11 @@ with lib;
         '';
       };
 
-      homeDir = mkOption {
-        default = "/var/lib/ddclient";
-        type = str;
-        description = "Home directory for the daemon user.";
-      };
-
-      domain = mkOption {
-        default = "";
-        type = str;
+      domains = mkOption {
+        default = [ "" ];
+        type = listOf str;
         description = ''
-          Domain name to synchronize.
+          Domain name(s) to synchronize.
         '';
       };
 
@@ -62,7 +56,7 @@ with lib;
         default = "";
         type = str;
         description = ''
-          Username.
+          User name.
         '';
       };
 
@@ -75,9 +69,12 @@ with lib;
       };
 
       interval = mkOption {
-        default = 600;
-        type = int;
-        description = "The interval at which to run the check and update.";
+        default = "10min";
+        type = str;
+        description = ''
+          The interval at which to run the check and update.
+          See <command>man 7 systemd.time</command> for the format.
+        '';
       };
 
       configFile = mkOption {
@@ -95,7 +92,7 @@ with lib;
         default = "dyndns2";
         type = str;
         description = ''
-          Protocol to use with dynamic DNS provider (see http://sourceforge.net/apps/trac/ddclient/wiki/Protocols).
+          Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
         '';
       };
 
@@ -115,11 +112,20 @@ with lib;
         '';
       };
 
-      extraConfig = mkOption {
+
+      quiet = mkOption {
+        default = false;
+        type = bool;
+        description = ''
+          Print no messages for unnecessary updates.
+        '';
+      };
+
+      script = mkOption {
         default = "";
-        type = lines;
+        type = str;
         description = ''
-          Extra configuration. Contents will be added verbatim to the configuration file.
+          script as required by some providers.
         '';
       };
 
@@ -139,11 +145,19 @@ with lib;
         '';
       };
 
-      quiet = mkOption {
-        default = false;
-        type = bool;
+      zone = mkOption {
+        default = "";
+        type = str;
         description = ''
-          Print no messages for unnecessary updates.
+          zone as required by some providers.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = lines;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
         '';
       };
     };
@@ -153,23 +167,8 @@ with lib;
   ###### implementation
 
   config = mkIf config.services.ddclient.enable {
-
-    users = {
-      extraGroups.ddclient.gid = config.ids.gids.ddclient;
-
-      extraUsers.ddclient = {
-        uid = config.ids.uids.ddclient;
-        description = "ddclient daemon user";
-        group = "ddclient";
-        home = cfg.homeDir;
-        createHome = true;
-      };
-    };
-
     environment.etc."ddclient.conf" = {
       enable = cfg.configFile == "/etc/ddclient.conf";
-      uid = config.ids.uids.ddclient;
-      gid = config.ids.gids.ddclient;
       mode = "0600";
       text = configText;
     };
@@ -180,15 +179,22 @@ with lib;
       after = [ "network.target" ];
       restartTriggers = [ config.environment.etc."ddclient.conf".source ];
 
-      serviceConfig = {
-        RuntimeDirectory = "ddclient";
-        # we cannot run in forking mode as it swallows all the program output
-        Type = "simple";
-        User = "ddclient";
-        Group = "ddclient";
-        ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -foreground -file ${cfg.configFile}";
-        ProtectSystem = "full";
-        PrivateTmp = true;
+      serviceConfig = rec {
+        DynamicUser = true;
+        RuntimeDirectory = StateDirectory;
+        StateDirectory = builtins.baseNameOf dataDir;
+        Type = "oneshot";
+        ExecStartPre = "!${lib.getBin pkgs.coreutils}/bin/install -m666 ${cfg.configFile} /run/${RuntimeDirectory}/ddclient.conf";
+        ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
+      };
+    };
+
+    systemd.timers.ddclient = {
+      description = "Run ddclient";
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnBootSec = cfg.interval;
+        OnUnitInactiveSec = cfg.interval;
       };
     };
   };
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index d283c7624335..de0aa1a2c2c3 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -16,7 +16,7 @@ let
   # Don't start dhcpcd on explicitly configured interfaces or on
   # interfaces that are part of a bridge, bond or sit device.
   ignoredInterfaces =
-    map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ip4 != [ ] || i.ipAddress != null) interfaces)
+    map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces)
     ++ mapAttrsToList (i: _: i) config.networking.sits
     ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges))
     ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.vswitches))
@@ -156,11 +156,11 @@ in
     systemd.services.dhcpcd = let
       cfgN = config.networking;
       hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "")
-                          || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != "");
+                          && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != ""));
     in
       { description = "DHCP Client";
 
-        wantedBy = optional (!hasDefaultGatewaySet) "network-online.target";
+        wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target";
         after = [ "network.target" ];
         wants = [ "network.target" ];
 
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
index 2eac6dfec5b7..fd7e317eee95 100644
--- a/nixos/modules/services/networking/dhcpd.nix
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -36,6 +36,7 @@ let
 
       preStart = ''
         mkdir -m 755 -p ${cfg.stateDir}
+        chown dhcpd:nogroup ${cfg.stateDir}
         touch ${cfg.stateDir}/dhcpd.leases
       '';
 
diff --git a/nixos/modules/services/networking/dnscache.nix b/nixos/modules/services/networking/dnscache.nix
index 379203cd1ab6..ba5c8e2d5e53 100644
--- a/nixos/modules/services/networking/dnscache.nix
+++ b/nixos/modules/services/networking/dnscache.nix
@@ -9,12 +9,12 @@ let
     mkdir -p $out/{servers,ip}
 
     ${concatMapStrings (ip: ''
-      echo > "$out/ip/"${lib.escapeShellArg ip}
+      touch "$out/ip/"${lib.escapeShellArg ip}
     '') cfg.clientIps}
 
     ${concatStrings (mapAttrsToList (host: ips: ''
       ${concatMapStrings (ip: ''
-        echo ${lib.escapeShellArg ip} > "$out/servers/"${lib.escapeShellArg host}
+        echo ${lib.escapeShellArg ip} >> "$out/servers/"${lib.escapeShellArg host}
       '') ips}
     '') cfg.domainServers)}
 
@@ -34,33 +34,49 @@ in {
 
   options = {
     services.dnscache = {
+
       enable = mkOption {
         default = false;
         type = types.bool;
-        description = "Whether to run the dnscache caching dns server";
+        description = "Whether to run the dnscache caching dns server.";
       };
 
       ip = mkOption {
         default = "0.0.0.0";
         type = types.str;
-        description = "IP address on which to listen for connections";
+        description = "IP address on which to listen for connections.";
       };
 
       clientIps = mkOption {
         default = [ "127.0.0.1" ];
         type = types.listOf types.str;
-        description = "client IP addresses (or prefixes) from which to accept connections";
+        description = "Client IP addresses (or prefixes) from which to accept connections.";
         example = ["192.168" "172.23.75.82"];
       };
 
       domainServers = mkOption {
         default = { };
         type = types.attrsOf (types.listOf types.str);
-        description = "table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts)";
+        description = ''
+          Table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts).
+          If entry for @ is not specified predefined list of root servers is used.
+        '';
         example = {
-          "example.com" = ["8.8.8.8" "8.8.4.4"];
+          "@" = ["8.8.8.8" "8.8.4.4"];
+          "example.com" = ["192.168.100.100"];
         };
       };
+
+      forwardOnly = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to treat root servers (for @) as caching
+          servers, requesting addresses the same way a client does. This is
+          needed if you want to use e.g. Google DNS as your upstream DNS.
+        '';
+      };
+
     };
   };
 
@@ -82,6 +98,7 @@ in {
       '';
       script = ''
         cd /var/lib/dnscache/
+        ${optionalString cfg.forwardOnly "export FORWARDONLY=1"}
         exec ./run
       '';
     };
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index ed658258c7f9..857657eea4db 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -10,7 +10,7 @@ let
   # This is somewhat more flexible than preloading the key as an
   # embedded string.
   upstreamResolverListPubKey = pkgs.fetchurl {
-    url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/minisign.pub;
+    url = https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/minisign.pub;
     sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh";
   };
 
@@ -258,9 +258,9 @@ in
         domain=raw.githubusercontent.com
         get="curl -fSs --resolve $domain:443:$(hostip -r 8.8.8.8 $domain | head -1)"
         $get -o dnscrypt-resolvers.csv.tmp \
-          https://$domain/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv
+          https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv
         $get -o dnscrypt-resolvers.csv.minisig.tmp \
-          https://$domain/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig
+          https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig
         mv dnscrypt-resolvers.csv.minisig{.tmp,}
         if ! minisign -q -V -p ${upstreamResolverListPubKey} \
           -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
index 23cc92946e41..bf13d5c6f5fe 100644
--- a/nixos/modules/services/networking/dnscrypt-wrapper.nix
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -145,6 +145,16 @@ in {
     };
     users.groups.dnscrypt-wrapper = { };
 
+    security.polkit.extraConfig = ''
+      // Allow dnscrypt-wrapper user to restart dnscrypt-wrapper.service
+      polkit.addRule(function(action, subject) {
+          if (action.id == "org.freedesktop.systemd1.manage-units" &&
+              action.lookup("unit") == "dnscrypt-wrapper.service" &&
+              subject.user == "dnscrypt-wrapper") {
+              return polkit.Result.YES;
+          }
+        });
+    '';
 
     systemd.services.dnscrypt-wrapper = {
       description = "dnscrypt-wrapper daemon";
diff --git a/nixos/modules/services/networking/firefox/sync-server.nix b/nixos/modules/services/networking/firefox/sync-server.nix
index a9f3fd65d76b..97d223a56cab 100644
--- a/nixos/modules/services/networking/firefox/sync-server.nix
+++ b/nixos/modules/services/networking/firefox/sync-server.nix
@@ -33,6 +33,8 @@ let
 in
 
 {
+  meta.maintainers = with lib.maintainers; [ nadrieril ];
+
   options = {
     services.firefox.syncserver = {
       enable = mkOption {
@@ -70,18 +72,6 @@ in
         '';
       };
 
-      user = mkOption {
-        type = types.str;
-        default = "syncserver";
-        description = "User account under which syncserver runs.";
-      };
-
-      group = mkOption {
-        type = types.str;
-        default = "syncserver";
-        description = "Group account under which syncserver runs.";
-      };
-
       publicUrl = mkOption {
         type = types.str;
         default = "http://localhost:5000/";
@@ -137,7 +127,9 @@ in
   config = mkIf cfg.enable {
 
     systemd.services.syncserver = let
-      syncServerEnv = pkgs.python.withPackages(ps: with ps; [ syncserver pasteScript ]);
+      syncServerEnv = pkgs.python.withPackages(ps: with ps; [ syncserver pasteScript requests ]);
+      user = "syncserver";
+      group = "syncserver";
     in {
       after = [ "network.target" ];
       description = "Firefox Sync Server";
@@ -145,43 +137,43 @@ in
       path = [ pkgs.coreutils syncServerEnv ];
 
       serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
+        User = user;
+        Group = group;
         PermissionsStartOnly = true;
       };
 
       preStart = ''
         if ! test -e ${cfg.privateConfig}; then
-          mkdir -m 700 -p $(dirname ${cfg.privateConfig})
+          mkdir -p $(dirname ${cfg.privateConfig})
           echo  > ${cfg.privateConfig} '[syncserver]'
+          chmod 600 ${cfg.privateConfig}
           echo >> ${cfg.privateConfig} "secret = $(head -c 20 /dev/urandom | sha1sum | tr -d ' -')"
         fi
-        chown ${cfg.user}:${cfg.group} ${cfg.privateConfig}
+        chmod 600 ${cfg.privateConfig}
+        chmod 755 $(dirname ${cfg.privateConfig})
+        chown ${user}:${group} ${cfg.privateConfig}
+
       '' + optionalString (cfg.sqlUri == defaultSqlUri) ''
         if ! test -e $(dirname ${defaultDbLocation}); then
           mkdir -m 700 -p $(dirname ${defaultDbLocation})
-          chown ${cfg.user}:${cfg.group} $(dirname ${defaultDbLocation})
+          chown ${user}:${group} $(dirname ${defaultDbLocation})
         fi
+
         # Move previous database file if it exists
         oldDb="/var/db/firefox-sync-server.db"
         if test -f $oldDb; then
           mv $oldDb ${defaultDbLocation}
-          chown ${cfg.user}:${cfg.group} ${defaultDbLocation}
+          chown ${user}:${group} ${defaultDbLocation}
         fi
       '';
       serviceConfig.ExecStart = "${syncServerEnv}/bin/paster serve ${syncServerIni}";
     };
 
-    users.extraUsers = optionalAttrs (cfg.user == "syncserver")
-      (singleton {
-        name = "syncserver";
-        group = cfg.group;
-        isSystemUser = true;
-      });
-
-    users.extraGroups = optionalAttrs (cfg.group == "syncserver")
-      (singleton {
-        name = "syncserver";
-      });
+    users.users.syncserver = {
+      group = "syncserver";
+      isSystemUser = true;
+    };
+
+    users.groups.syncserver = {};
   };
 }
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 9bd88ca1707b..20c0b0acf165 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -54,7 +54,7 @@ let
     '';
 
   writeShScript = name: text: let dir = pkgs.writeScriptBin name ''
-    #! ${pkgs.stdenv.shell} -e
+    #! ${pkgs.runtimeShell} -e
     ${text}
   ''; in "${dir}/bin/${name}";
 
@@ -125,6 +125,9 @@ let
       ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
       ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN
 
+      # Allows this host to act as a DHCP4 client without first having to use APIPA
+      iptables -t raw -A nixos-fw-rpfilter -p udp --sport 67 --dport 68 -j RETURN
+
       # Allows this host to act as a DHCPv4 server
       iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
 
diff --git a/nixos/modules/services/networking/flashpolicyd.nix b/nixos/modules/services/networking/flashpolicyd.nix
index 5ba85178179b..5b83ce131389 100644
--- a/nixos/modules/services/networking/flashpolicyd.nix
+++ b/nixos/modules/services/networking/flashpolicyd.nix
@@ -22,7 +22,7 @@ let
 
   flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd"
     ''
-      #! ${pkgs.stdenv.shell}
+      #! ${pkgs.runtimeShell}
       exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \
         --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \
         2> /dev/null
diff --git a/nixos/modules/services/networking/freeradius.nix b/nixos/modules/services/networking/freeradius.nix
new file mode 100644
index 000000000000..45cba1ce2770
--- /dev/null
+++ b/nixos/modules/services/networking/freeradius.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.freeradius;
+
+  freeradiusService = cfg:
+  {
+    description = "FreeRadius server";
+    wantedBy = ["multi-user.target"];
+    after = ["network-online.target"];
+    wants = ["network-online.target"];
+    preStart = ''
+      ${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout
+    '';
+
+    serviceConfig = {
+        ExecStart = "${pkgs.freeradius}/bin/radiusd -f -d ${cfg.configDir} -l stdout -xx";
+        ExecReload = [
+          "${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout"
+          "${pkgs.coreutils}/bin/kill -HUP $MAINPID"
+        ];
+        User = "radius";
+        ProtectSystem = "full";
+        ProtectHome = "on";
+        Restart = "on-failure";
+        RestartSec = 2;
+    };
+  };
+
+  freeradiusConfig = {
+    enable = mkEnableOption "the freeradius server";
+
+    configDir = mkOption {
+      type = types.path;
+      default = "/etc/raddb";
+      description = ''
+        The path of the freeradius server configuration directory.
+      '';
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.freeradius = freeradiusConfig;
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.enable) {
+
+    users = {
+      extraUsers.radius = {
+        /*uid = config.ids.uids.radius;*/
+        description = "Radius daemon user";
+      };
+    };
+
+    systemd.services.freeradius = freeradiusService cfg;
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/gnunet.nix b/nixos/modules/services/networking/gnunet.nix
index 03ee54af4334..02cd53c6fa38 100644
--- a/nixos/modules/services/networking/gnunet.nix
+++ b/nixos/modules/services/networking/gnunet.nix
@@ -137,6 +137,8 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.gnunet pkgs.miniupnpc ];
+      environment.TMPDIR = "/tmp";
+      serviceConfig.PrivateTemp = true;
       serviceConfig.ExecStart = "${pkgs.gnunet}/lib/gnunet/libexec/gnunet-service-arm -c ${configFile}";
       serviceConfig.User = "gnunet";
       serviceConfig.UMask = "0007";
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index ca2e2a065dcf..8f5aeee4a16b 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -126,6 +126,7 @@ let
         [${tun.name}]
         type = client
         destination = ${tun.destination}
+        destinationport = ${toString tun.destinationPort}
         keys = ${tun.keys}
         address = ${tun.address}
         port = ${toString tun.port}
@@ -137,15 +138,15 @@ let
       '')
     }
     ${flip concatMapStrings
-      (collect (tun: tun ? port && tun ? host) cfg.inTunnels)
-      (tun: let portStr = toString tun.port; in ''
+      (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
+      (tun: ''
         [${tun.name}]
         type = server
         destination = ${tun.destination}
         keys = ${tun.keys}
         host = ${tun.address}
-        port = ${tun.port}
-        inport = ${tun.inPort}
+        port = ${toString tun.port}
+        inport = ${toString tun.inPort}
         accesslist = ${builtins.concatStringsSep "," tun.accessList}
       '')
     }
@@ -405,7 +406,13 @@ in
         default = {};
         type = with types; loaOf (submodule (
           { name, config, ... }: {
-            options = commonTunOpts name;
+            options = {
+              destinationPort = mkOption {
+                type = types.int;
+                default = 0;
+                description = "Connect to particular port at destination.";
+              };
+            } // commonTunOpts name;
             config = {
               name = mkDefault name;
             };
diff --git a/nixos/modules/services/networking/iwd.nix b/nixos/modules/services/networking/iwd.nix
index 23787bce9911..344212ad8329 100644
--- a/nixos/modules/services/networking/iwd.nix
+++ b/nixos/modules/services/networking/iwd.nix
@@ -26,7 +26,7 @@ in {
       wants = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
-      serviceConfig.ExecStart = "${pkgs.iwd}/bin/iwd";
+      serviceConfig.ExecStart = "${pkgs.iwd}/libexec/iwd";
     };
   };
 
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
index 18e2ab9aebf1..aac02b811d71 100644
--- a/nixos/modules/services/networking/kresd.nix
+++ b/nixos/modules/services/networking/kresd.nix
@@ -43,7 +43,16 @@ in
       type = with types; listOf str;
       default = [ "::1" "127.0.0.1" ];
       description = ''
-        What addresses the server should listen on.
+        What addresses the server should listen on. (UDP+TCP 53)
+      '';
+    };
+    listenTLS = mkOption {
+      type = with types; listOf str;
+      default = [];
+      example = [ "198.51.100.1:853" "[2001:db8::1]:853" "853" ];
+      description = ''
+        Addresses on which kresd should provide DNS over TLS (see RFC 7858).
+        For detailed syntax see ListenStream in man systemd.socket.
       '';
     };
     # TODO: perhaps options for more common stuff like cache size or forwarding
@@ -72,6 +81,19 @@ in
         (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
         cfg.interfaces;
       socketConfig.ListenDatagram = listenStreams;
+      socketConfig.FreeBind = true;
+    };
+
+    systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec {
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      partOf = [ "kresd.socket" ];
+      listenStreams = cfg.listenTLS;
+      socketConfig = {
+        FileDescriptorName = "tls";
+        FreeBind = true;
+        Service = "kresd.service";
+      };
     };
 
     systemd.sockets.kresd-control = rec {
@@ -82,20 +104,11 @@ in
       socketConfig = {
         FileDescriptorName = "control";
         Service = "kresd.service";
-        SocketMode = "0660"; # only root user/group may connect
+        SocketMode = "0660"; # only root user/group may connect and control kresd
       };
     };
 
-    # Create the cacheDir; tmpfiles don't work on nixos-rebuild switch.
-    systemd.services.kresd-cachedir = {
-      serviceConfig.Type = "oneshot";
-      script = ''
-        if [ ! -d '${cfg.cacheDir}' ]; then
-          mkdir -p '${cfg.cacheDir}'
-          chown kresd:kresd '${cfg.cacheDir}'
-        fi
-      '';
-    };
+    systemd.tmpfiles.rules = [ "d '${cfg.cacheDir}' 0770 kresd kresd - -" ];
 
     systemd.services.kresd = {
       description = "Knot-resolver daemon";
@@ -104,16 +117,17 @@ in
         User = "kresd";
         Type = "notify";
         WorkingDirectory = cfg.cacheDir;
+        Restart = "on-failure";
+        Sockets = [ "kresd.socket" "kresd-control.socket" ]
+          ++ optional (cfg.listenTLS != []) "kresd-tls.socket";
       };
 
+      # Trust anchor goes from dns-root-data by default.
       script = ''
-        exec '${package}/bin/kresd' --config '${configFile}' \
-          -k '${cfg.cacheDir}/root.key'
+        exec '${package}/bin/kresd' --config '${configFile}' --forks=1
       '';
 
-      after = [ "kresd-cachedir.service" ];
-      requires = [ "kresd.socket" "kresd-cachedir.service" ];
-      wantedBy = [ "sockets.target" ];
+      requires = [ "kresd.socket" ];
     };
   };
 }
diff --git a/nixos/modules/services/networking/lldpd.nix b/nixos/modules/services/networking/lldpd.nix
index ba4e1b1542fe..db1534edfd7c 100644
--- a/nixos/modules/services/networking/lldpd.nix
+++ b/nixos/modules/services/networking/lldpd.nix
@@ -24,6 +24,7 @@ in
       description = "lldpd user";
       group = "_lldpd";
       home = "/var/run/lldpd";
+      isSystemUser = true;
     };
     users.extraGroups._lldpd = {};
 
diff --git a/nixos/modules/services/networking/monero.nix b/nixos/modules/services/networking/monero.nix
new file mode 100644
index 000000000000..31379189f5de
--- /dev/null
+++ b/nixos/modules/services/networking/monero.nix
@@ -0,0 +1,238 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg     = config.services.monero;
+  dataDir = "/var/lib/monero";
+
+  listToConf = option: list:
+    concatMapStrings (value: "${option}=${value}\n") list;
+
+  login = (cfg.rpc.user != null && cfg.rpc.password != null);
+
+  configFile = with cfg; pkgs.writeText "monero.conf" ''
+    log-file=/dev/stdout
+    data-dir=${dataDir}
+
+    ${optionalString mining.enable ''
+      start-mining=${mining.address}
+      mining-threads=${toString mining.threads}
+    ''}
+
+    rpc-bind-ip=${rpc.address}
+    rpc-bind-port=${toString rpc.port}
+    ${optionalString login ''
+      rpc-login=${rpc.user}:${rpc.password}
+    ''}
+    ${optionalString rpc.restricted ''
+      restrict-rpc=1
+    ''}
+
+    limit-rate-up=${toString limits.upload}
+    limit-rate-down=${toString limits.download}
+    max-concurrency=${toString limits.threads}
+    block-sync-size=${toString limits.syncSize}
+
+    ${listToConf "add-peer" extraNodes}
+    ${listToConf "add-priority-node" priorityNodes}
+    ${listToConf "add-exclusive-node" exclusiveNodes}
+
+    ${extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.monero = {
+
+      enable = mkEnableOption "Monero node daemon.";
+
+      mining.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to mine moneroj.
+        '';
+      };
+
+      mining.address = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Monero address where to send mining rewards.
+        '';
+      };
+
+      mining.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Number of threads used for mining.
+          Set to <literal>0</literal> to use all available.
+        '';
+      };
+
+      rpc.user = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          User name for RPC connections.
+        '';
+      };
+
+      rpc.password = mkOption {
+        type = types.str;
+        default = null;
+        description = ''
+          Password for RPC connections.
+        '';
+      };
+
+      rpc.address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          IP address the RPC server will bind to.
+        '';
+      };
+
+      rpc.port = mkOption {
+        type = types.int;
+        default = 18081;
+        description = ''
+          Port the RPC server will bind to.
+        '';
+      };
+
+      rpc.restricted = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to restrict RPC to view only commands.
+        '';
+      };
+
+      limits.upload = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the upload rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.download = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the download rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of threads used for a parallel job.
+          Set to <literal>0</literal> to leave unlimited.
+        '';
+      };
+
+      limits.syncSize = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of blocks to sync at once.
+          Set to <literal>0</literal> for adaptive.
+        '';
+      };
+
+      extraNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of additional peer IP addresses to add to the local list.
+        '';
+      };
+
+      priorityNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to and
+          attempt to keep the connection open.
+        '';
+      };
+
+      exclusiveNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to *only*.
+          If given the other peer options will be ignored.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to monerod configuration.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton {
+      name = "monero";
+      uid  = config.ids.uids.monero;
+      description = "Monero daemon user";
+      home = dataDir;
+      createHome = true;
+    };
+
+    users.extraGroups = singleton {
+      name = "monero";
+      gid  = config.ids.gids.monero;
+    };
+
+    systemd.services.monero = {
+      description = "monero daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User  = "monero";
+        Group = "monero";
+        ExecStart = "${pkgs.monero}/bin/monerod --config-file=${configFile} --non-interactive";
+        Restart = "always";
+        SuccessExitStatus = [ 0 1 ];
+      };
+    };
+
+   assertions = singleton {
+     assertion = cfg.mining.enable -> cfg.mining.address != "";
+     message   = ''
+       You need a Monero address to receive mining rewards:
+       specify one using option monero.mining.address.
+    '';
+   };
+
+  };
+
+}
+
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 81915b5a2ef8..d8135f4d0ffa 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -12,6 +12,10 @@ let
     keyfile ${cfg.ssl.keyfile}
   '';
 
+  passwordConf = optionalString cfg.checkPasswords ''
+    password_file ${cfg.dataDir}/passwd
+  '';
+
   mosquittoConf = pkgs.writeText "mosquitto.conf" ''
     pid_file /run/mosquitto/pid
     acl_file ${aclFile}
@@ -19,6 +23,7 @@ let
     allow_anonymous ${boolToString cfg.allowAnonymous}
     bind_address ${cfg.host}
     port ${toString cfg.port}
+    ${passwordConf}
     ${listenerConf}
     ${cfg.extraConf}
   '';
@@ -153,6 +158,15 @@ in
         '';
       };
 
+      checkPasswords = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Refuse connection when clients provide incorrect passwords.
+        '';
+      };
+
       extraConf = mkOption {
         default = "";
         type = types.lines;
@@ -198,7 +212,7 @@ in
       '' + concatStringsSep "\n" (
         mapAttrsToList (n: c:
           if c.hashedPassword != null then
-            "echo '${n}:${c.hashedPassword}' > ${cfg.dataDir}/passwd"
+            "echo '${n}:${c.hashedPassword}' >> ${cfg.dataDir}/passwd"
           else optionalString (c.password != null)
             "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} ${c.password}"
         ) cfg.users);
diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix
index 13d7c3254f9d..873d62dbf341 100644
--- a/nixos/modules/services/networking/murmur.nix
+++ b/nixos/modules/services/networking/murmur.nix
@@ -80,7 +80,7 @@ in
 
       pidfile = mkOption {
         type = types.path;
-        default = "/tmp/murmurd.pid";
+        default = "/run/murmur/murmurd.pid";
         description = "Path to PID file for Murmur daemon.";
       };
 
@@ -252,6 +252,7 @@ in
 
       serviceConfig = {
         Type      = "forking";
+        RuntimeDirectory = "murmur";
         PIDFile   = cfg.pidfile;
         Restart   = "always";
         User      = "murmur";
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index 366bb2ed7a80..da3827c35e63 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -19,6 +19,8 @@ let
     iptables -w -t nat -D POSTROUTING -j nixos-nat-post 2>/dev/null || true
     iptables -w -t nat -F nixos-nat-post 2>/dev/null || true
     iptables -w -t nat -X nixos-nat-post 2>/dev/null || true
+
+    ${cfg.extraStopCommands}
   '';
 
   setupNat = ''
@@ -51,8 +53,40 @@ let
         -i ${cfg.externalInterface} -p ${fwd.proto} \
         --dport ${builtins.toString fwd.sourcePort} \
         -j DNAT --to-destination ${fwd.destination}
+
+      ${concatMapStrings (loopbackip:
+        let
+          m                = builtins.match "([0-9.]+):([0-9-]+)" fwd.destination;
+          destinationIP    = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0;
+          destinationPorts = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 1;
+        in ''
+          # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself
+          iptables -w -t nat -A OUTPUT \
+            -d ${loopbackip} -p ${fwd.proto} \
+            --dport ${builtins.toString fwd.sourcePort} \
+            -j DNAT --to-destination ${fwd.destination}
+
+          # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from other hosts behind NAT
+          iptables -w -t nat -A nixos-nat-pre \
+            -d ${loopbackip} -p ${fwd.proto} \
+            --dport ${builtins.toString fwd.sourcePort} \
+            -j DNAT --to-destination ${fwd.destination}
+
+          iptables -w -t nat -A nixos-nat-post \
+            -d ${destinationIP} -p ${fwd.proto} \
+            --dport ${destinationPorts} \
+            -j SNAT --to-source ${loopbackip}
+        '') fwd.loopbackIPs}
     '') cfg.forwardPorts}
 
+    ${optionalString (cfg.dmzHost != null) ''
+      iptables -w -t nat -A nixos-nat-pre \
+        -i ${cfg.externalInterface} -j DNAT \
+        --to-destination ${cfg.dmzHost}
+    ''}
+
+    ${cfg.extraCommands}
+
     # Append our chains to the nat tables
     iptables -w -t nat -A PREROUTING -j nixos-nat-pre
     iptables -w -t nat -A POSTROUTING -j nixos-nat-post
@@ -125,15 +159,15 @@ in
       type = with types; listOf (submodule {
         options = {
           sourcePort = mkOption {
-            type = types.int;
+            type = types.either types.int (types.strMatching "[[:digit:]]+:[[:digit:]]+");
             example = 8080;
-            description = "Source port of the external interface";
+            description = "Source port of the external interface; to specify a port range, use a string with a colon (e.g. \"60000:61000\")";
           };
 
           destination = mkOption {
             type = types.str;
             example = "10.0.0.1:80";
-            description = "Forward connection to destination ip:port";
+            description = "Forward connection to destination ip:port; to specify a port range, use ip:start-end";
           };
 
           proto = mkOption {
@@ -142,6 +176,13 @@ in
             example = "udp";
             description = "Protocol of forwarded connection";
           };
+
+          loopbackIPs = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = literalExample ''[ "55.1.2.3" ]'';
+            description = "Public IPs for NAT reflection; for connections to `loopbackip:sourcePort' from the host itself and from other hosts behind NAT";
+          };
         };
       });
       default = [];
@@ -153,6 +194,39 @@ in
         '';
     };
 
+    networking.nat.dmzHost = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "10.0.0.1";
+      description =
+        ''
+          The local IP address to which all traffic that does not match any
+          forwarding rule is forwarded.
+        '';
+    };
+
+    networking.nat.extraCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = "iptables -A INPUT -p icmp -j ACCEPT";
+      description =
+        ''
+          Additional shell commands executed as part of the nat
+          initialisation script.
+        '';
+    };
+
+    networking.nat.extraStopCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = "iptables -D INPUT -p icmp -j ACCEPT || true";
+      description =
+        ''
+          Additional shell commands executed as part of the nat
+          teardown script.
+        '';
+    };
+
   };
 
 
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 6bdae32f72bb..10e96eb40362 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -133,10 +133,9 @@ in {
       basePackages = mkOption {
         type = types.attrsOf types.package;
         default = { inherit networkmanager modemmanager wpa_supplicant
-                            networkmanager_openvpn networkmanager_vpnc
-                            networkmanager_openconnect networkmanager_fortisslvpn
-                            networkmanager_pptp networkmanager_l2tp
-                            networkmanager_iodine; };
+                            networkmanager-openvpn networkmanager-vpnc
+                            networkmanager-openconnect networkmanager-fortisslvpn
+                            networkmanager-l2tp networkmanager-iodine; };
         internal = true;
       };
 
@@ -241,6 +240,19 @@ in {
           A list of scripts which will be executed in response to  network  events.
         '';
       };
+
+      enableStrongSwan = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the StrongSwan plugin.
+          </para><para>
+          If you enable this option the
+          <literal>networkmanager_strongswan</literal> plugin will be added to
+          the <option>networking.networkmanager.packages</option> option
+          so you don't need to to that yourself.
+        '';
+      };
     };
   };
 
@@ -254,34 +266,29 @@ in {
       message = "You can not use networking.networkmanager with networking.wireless";
     }];
 
-    boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
-
     environment.etc = with cfg.basePackages; [
       { source = configFile;
         target = "NetworkManager/NetworkManager.conf";
       }
-      { source = "${networkmanager_openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
+      { source = "${networkmanager-openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
         target = "NetworkManager/VPN/nm-openvpn-service.name";
       }
-      { source = "${networkmanager_vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
+      { source = "${networkmanager-vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
         target = "NetworkManager/VPN/nm-vpnc-service.name";
       }
-      { source = "${networkmanager_openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
+      { source = "${networkmanager-openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
         target = "NetworkManager/VPN/nm-openconnect-service.name";
       }
-      { source = "${networkmanager_fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name";
+      { source = "${networkmanager-fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name";
         target = "NetworkManager/VPN/nm-fortisslvpn-service.name";
       }
-      { source = "${networkmanager_pptp}/etc/NetworkManager/VPN/nm-pptp-service.name";
-        target = "NetworkManager/VPN/nm-pptp-service.name";
-      }
-      { source = "${networkmanager_l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
+      { source = "${networkmanager-l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
         target = "NetworkManager/VPN/nm-l2tp-service.name";
       }
       { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name";
         target = "NetworkManager/VPN/nm-strongswan-service.name";
       }
-      { source = "${networkmanager_iodine}/etc/NetworkManager/VPN/nm-iodine-service.name";
+      { source = "${networkmanager-iodine}/etc/NetworkManager/VPN/nm-iodine-service.name";
         target = "NetworkManager/VPN/nm-iodine-service.name";
       }
     ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
@@ -322,6 +329,7 @@ in {
 
       preStart = ''
         mkdir -m 700 -p /etc/NetworkManager/system-connections
+        mkdir -m 700 -p /etc/ipsec.d
         mkdir -m 755 -p ${stateDirs}
       '';
     };
@@ -333,13 +341,13 @@ in {
       wireless.enable = lib.mkDefault false;
     };
 
-    powerManagement.resumeCommands = ''
-      ${config.systemd.package}/bin/systemctl restart network-manager
-    '';
-
     security.polkit.extraConfig = polkitConf;
 
-    services.dbus.packages = cfg.packages;
+    networking.networkmanager.packages =
+      mkIf cfg.enableStrongSwan [ pkgs.networkmanager_strongswan ];
+
+    services.dbus.packages =
+      optional cfg.enableStrongSwan pkgs.strongswanNM ++ cfg.packages;
 
     services.udev.packages = cfg.packages;
   };
diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix
index 56b942054140..ad7c013a5449 100644
--- a/nixos/modules/services/networking/nftables.nix
+++ b/nixos/modules/services/networking/nftables.nix
@@ -116,7 +116,7 @@ in
           include "${cfg.rulesetFile}"
         '';
         checkScript = pkgs.writeScript "nftables-check" ''
-          #! ${pkgs.stdenv.shell} -e
+          #! ${pkgs.runtimeShell} -e
           if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then
             echo "Unload ip_tables before using nftables!" 1>&2
             exit 1
diff --git a/nixos/modules/services/networking/nix-serve.nix b/nixos/modules/services/networking/nix-serve.nix
index 3e865e3b76a8..8499e7c0f7c4 100644
--- a/nixos/modules/services/networking/nix-serve.nix
+++ b/nixos/modules/services/networking/nix-serve.nix
@@ -55,6 +55,8 @@ in
       environment.NIX_SECRET_KEY_FILE = cfg.secretKeyFile;
 
       serviceConfig = {
+        Restart = "always";
+        RestartSec = "5s";
         ExecStart = "${pkgs.nix-serve}/bin/nix-serve " +
           "--listen ${cfg.bindAddress}:${toString cfg.port} ${cfg.extraParams}";
         User = "nix-serve";
diff --git a/nixos/modules/services/networking/nixops-dns.nix b/nixos/modules/services/networking/nixops-dns.nix
new file mode 100644
index 000000000000..2bb1263b7fa2
--- /dev/null
+++ b/nixos/modules/services/networking/nixops-dns.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  pkg = pkgs.nixops-dns;
+  cfg = config.services.nixops-dns;
+in
+
+{
+  options = {
+    services.nixops-dns = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the nixops-dns resolution
+          of NixOps virtual machines via dnsmasq and fake domain name.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user the nixops-dns daemon should run as.
+          This should be the user, which is also used for nixops and
+          have the .nixops directory in its home.
+        '';
+      };
+
+      domain = mkOption {
+        type = types.str;
+        description = ''
+          Fake domain name to resolve to NixOps virtual machines.
+
+          For example "ops" will resolve "vm.ops".
+        '';
+        example = "ops";
+        default = "ops";
+      };
+
+      dnsmasq = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enable dnsmasq forwarding to nixops-dns. This allows to use
+          nixops-dns for `services.nixops-dns.domain` resolution
+          while forwarding the rest of the queries to original resolvers.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.nixops-dns = {
+      description = "nixops-dns: DNS server for resolving NixOps machines";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+        User = cfg.user;
+        ExecStart="${pkg}/bin/nixops-dns --domain=.${cfg.domain}";
+      };
+    };
+
+    services.dnsmasq = mkIf cfg.dnsmasq {
+      enable = true;
+      resolveLocalQueries = true;
+      servers = [
+        "/${cfg.domain}/127.0.0.1#5300"
+      ];
+      extraConfig = ''
+        bind-interfaces
+        listen-address=127.0.0.1
+      '';
+    };
+
+  };
+}
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index c8b8ed547ebb..4241e6fcceab 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -11,6 +11,8 @@ let
 
   # build nsd with the options needed for the given config
   nsdPkg = pkgs.nsd.override {
+    configFile = "${configFile}/nsd.conf";
+
     bind8Stats = cfg.bind8Stats;
     ipv6 = cfg.ipv6;
     ratelimit = cfg.ratelimit.enable;
@@ -788,6 +790,8 @@ in
 
   config = mkIf cfg.enable {
 
+    environment.systemPackages = [ nsdPkg ];
+
     users.extraGroups = singleton {
       name = username;
       gid = config.ids.gids.nsd;
@@ -845,4 +849,6 @@ in
     };
 
   };
+
+  meta.maintainers = with lib.maintainers; [ hrdinka ];
 }
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
index 3fbf5a9f0227..a418839d22b8 100644
--- a/nixos/modules/services/networking/openvpn.nix
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -50,6 +50,11 @@ let
               "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
           ${optionalString (cfg.down != "" || cfg.updateResolvConf)
               "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
+          ${optionalString (cfg.authUserPass != null)
+              "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
+                ${cfg.authUserPass.username}
+                ${cfg.authUserPass.password}
+              ''}"}
         '';
 
     in {
@@ -60,7 +65,7 @@ let
 
       path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
 
-      serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --config ${configFile}";
+      serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}";
       serviceConfig.Restart = "always";
       serviceConfig.Type = "notify";
     };
@@ -161,6 +166,29 @@ in
             '';
           };
 
+          authUserPass = mkOption {
+            default = null;
+            description = ''
+              This option can be used to store the username / password credentials
+              with the "auth-user-pass" authentication method.
+
+              WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
+            '';
+            type = types.nullOr (types.submodule {
+
+              options = {
+                username = mkOption {
+                  description = "The username to store inside the credentials file.";
+                  type = types.string;
+                };
+
+                password = mkOption {
+                  description = "The password to store inside the credentials file.";
+                  type = types.string;
+                };
+              };
+            });
+          };
         };
 
       });
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index fb9c9dc67f24..1b4f81f6b56e 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -10,98 +10,229 @@ let
 
     options = {
 
-      # TODO: require attribute
       key = mkOption {
-        type = types.str;
-        description = "Path to the key file";
+        type = types.path;
+        description = "Path to the key file.";
       };
 
-      # TODO: require attribute
+      # TODO: rename to certificate to match the prosody config
       cert = mkOption {
-        type = types.str;
-        description = "Path to the certificate file";
+        type = types.path;
+        description = "Path to the certificate file.";
       };
+
+      extraOptions = mkOption {
+        type = types.attrs;
+        default = {};
+        description = "Extra SSL configuration options.";
+      };
+
     };
   };
 
   moduleOpts = {
-
+    # Generally required
     roster = mkOption {
+      type = types.bool;
       default = true;
       description = "Allow users to have a roster";
     };
 
     saslauth = mkOption {
+      type = types.bool;
       default = true;
       description = "Authentication for clients and servers. Recommended if you want to log in.";
     };
 
     tls = mkOption {
+      type = types.bool;
       default = true;
       description = "Add support for secure TLS on c2s/s2s connections";
     };
 
     dialback = mkOption {
+      type = types.bool;
       default = true;
       description = "s2s dialback support";
     };
 
     disco = mkOption {
+      type = types.bool;
       default = true;
       description = "Service discovery";
     };
 
-    legacyauth = mkOption {
+    # Not essential, but recommended
+    carbons = mkOption {
+      type = types.bool;
       default = true;
-      description = "Legacy authentication. Only used by some old clients and bots";
+      description = "Keep multiple clients in sync";
     };
 
+    pep = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enables users to publish their mood, activity, playing music and more";
+    };
+
+    private = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Private XML storage (for room bookmarks, etc.)";
+    };
+
+    blocklist = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to block communications with other users";
+    };
+
+    vcard = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to set vCards";
+    };
+
+    # Nice to have
     version = mkOption {
+      type = types.bool;
       default = true;
       description = "Replies to server version requests";
     };
 
     uptime = mkOption {
+      type = types.bool;
       default = true;
       description = "Report how long server has been running";
     };
 
     time = mkOption {
+      type = types.bool;
       default = true;
       description = "Let others know the time here on this server";
     };
 
     ping = mkOption {
+      type = types.bool;
       default = true;
       description = "Replies to XMPP pings with pongs";
     };
 
-    console = mkOption {
+    register = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to register on this server using a client and change passwords";
+    };
+
+    mam = mkOption {
+      type = types.bool;
       default = false;
-      description = "telnet to port 5582";
+      description = "Store messages in an archive and allow users to access it";
     };
 
+    # Admin interfaces
+    admin_adhoc = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allows administration via an XMPP client that supports ad-hoc commands";
+    };
+
+    admin_telnet = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Opens telnet console interface on localhost port 5582";
+    };
+
+    # HTTP modules
     bosh = mkOption {
+      type = types.bool;
       default = false;
       description = "Enable BOSH clients, aka 'Jabber over HTTP'";
     };
 
-    httpserver = mkOption {
+    websocket = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable WebSocket support";
+    };
+
+    http_files = mkOption {
+      type = types.bool;
       default = false;
       description = "Serve static files from a directory over HTTP";
     };
 
-    websocket = mkOption {
+    # Other specific functionality
+    limits = mkOption {
+      type = types.bool;
       default = false;
-      description = "Enable WebSocket support";
+      description = "Enable bandwidth limiting for XMPP connections";
+    };
+
+    groups = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Shared roster support";
+    };
+
+    server_contact_info = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Publish contact information for this service";
+    };
+
+    announce = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send announcement to all online users";
+    };
+
+    welcome = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Welcome users who register accounts";
+    };
+
+    watchregistrations = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Alert admins of registrations";
+    };
+
+    motd = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send a message to users when they log in";
+    };
+
+    legacyauth = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Legacy authentication. Only used by some old clients and bots";
+    };
+
+    proxy65 = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enables a file transfer proxy service which clients behind NAT can use";
     };
 
   };
 
-  createSSLOptsStr = o:
-    if o ? key && o ? cert then
-      ''ssl = { key = "${o.key}"; certificate = "${o.cert}"; };''
-    else "";
+  toLua = x:
+    if builtins.isString x then ''"${x}"''
+    else if builtins.isBool x then (if x == true then "true" else "false")
+    else if builtins.isInt x then toString x
+    else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }''
+    else throw "Invalid Lua value";
+
+  createSSLOptsStr = o: ''
+    ssl = {
+      key = "${o.key}";
+      certificate = "${o.cert}";
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name} = ${toLua value};") o.extraOptions)}
+    };
+  '';
 
   vHostOpts = { ... }: {
 
@@ -114,18 +245,20 @@ let
       };
 
       enabled = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to enable the virtual host";
       };
 
       ssl = mkOption {
-        description = "Paths to SSL files";
+        type = types.nullOr (types.submodule sslOpts);
         default = null;
-        options = [ sslOpts ];
+        description = "Paths to SSL files";
       };
 
       extraConfig = mkOption {
-        default = '''';
+        type = types.lines;
+        default = "";
         description = "Additional virtual host specific configuration";
       };
 
@@ -144,20 +277,113 @@ in
     services.prosody = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to enable the prosody server";
       };
 
+      package = mkOption {
+        type = types.package;
+        description = "Prosody package to use";
+        default = pkgs.prosody;
+        defaultText = "pkgs.prosody";
+        example = literalExample ''
+          pkgs.prosody.override {
+            withExtraLibs = [ pkgs.luaPackages.lpty ];
+            withCommunityModules = [ "auth_external" ];
+          };
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.string;
+        description = "Directory where Prosody stores its data";
+        default = "/var/lib/prosody";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "prosody";
+        description = "User account under which prosody runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "prosody";
+        description = "Group account under which prosody runs.";
+      };
+
       allowRegistration = mkOption {
+        type = types.bool;
         default = false;
         description = "Allow account creation";
       };
 
+      c2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force clients to use encrypted connections? This option will
+          prevent clients from authenticating unless they are using encryption.
+        '';
+      };
+
+      s2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force servers to use encrypted connections? This option will
+          prevent servers from authenticating unless they are using encryption.
+          Note that this is different from authentication.
+        '';
+      };
+
+      s2sSecureAuth = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Force certificate authentication for server-to-server connections?
+          This provides ideal security, but requires servers you communicate
+          with to support encryption AND present valid, trusted certificates.
+          For more information see https://prosody.im/doc/s2s#security
+        '';
+      };
+
+      s2sInsecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "insecure.example.com" ];
+        description = ''
+          Some servers have invalid or self-signed certificates. You can list
+          remote domains here that will not be required to authenticate using
+          certificates. They will be authenticated using DNS instead, even
+          when s2s_secure_auth is enabled.
+        '';
+      };
+
+      s2sSecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "jabber.org" ];
+        description = ''
+          Even if you leave s2s_secure_auth disabled, you can still require valid
+          certificates for some domains by specifying a list here.
+        '';
+      };
+
+
       modules = moduleOpts;
 
       extraModules = mkOption {
+        type = types.listOf types.str;
+        default = [];
         description = "Enable custom modules";
+      };
+
+      extraPluginPaths = mkOption {
+        type = types.listOf types.path;
         default = [];
+        description = "Addtional path in which to look find plugins/modules";
       };
 
       virtualHosts = mkOption {
@@ -183,20 +409,21 @@ in
       };
 
       ssl = mkOption {
-        description = "Paths to SSL files";
+        type = types.nullOr (types.submodule sslOpts);
         default = null;
-        options = [ sslOpts ];
+        description = "Paths to SSL files";
       };
 
       admins = mkOption {
-        description = "List of administrators of the current host";
-        example = [ "admin1@example.com" "admin2@example.com" ];
+        type = types.listOf types.str;
         default = [];
+        example = [ "admin1@example.com" "admin2@example.com" ];
+        description = "List of administrators of the current host";
       };
 
       extraConfig = mkOption {
         type = types.lines;
-        default = '''';
+        default = "";
         description = "Additional prosody configuration";
       };
 
@@ -208,37 +435,47 @@ in
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.prosody ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc."prosody/prosody.cfg.lua".text = ''
 
-      pidfile = "/var/lib/prosody/prosody.pid"
-
+      pidfile = "/run/prosody/prosody.pid"
 
       log = "*syslog"
 
-      data_path = "/var/lib/prosody"
-
-      allow_registration = ${boolToString cfg.allowRegistration};
-
-      ${ optionalString cfg.modules.console "console_enabled = true;" }
+      data_path = "${cfg.dataDir}"
+      plugin_paths = {
+        ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) }
+      }
 
       ${ optionalString  (cfg.ssl != null) (createSSLOptsStr cfg.ssl) }
 
-      admins = { ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.admins) } };
+      admins = ${toLua cfg.admins}
+
+      -- we already build with libevent, so we can just enable it for a more performant server
+      use_libevent = true
 
       modules_enabled = {
 
         ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList
-          (name: val: optionalString val ''"${name}";'')
+          (name: val: optionalString val "${toLua name};")
         cfg.modules) }
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)}
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)}
+      };
 
-        ${ optionalString cfg.allowRegistration "\"register\"\;" }
+      allow_registration = ${toLua cfg.allowRegistration}
 
-        ${ lib.concatStringsSep "\n" (map (x: "\"${x}\";") cfg.extraModules)}
+      c2s_require_encryption = ${toLua cfg.c2sRequireEncryption}
+
+      s2s_require_encryption = ${toLua cfg.s2sRequireEncryption}
+
+      s2s_secure_auth = ${toLua cfg.s2sSecureAuth}
+
+      s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains}
+
+      s2s_secure_domains = ${toLua cfg.s2sSecureDomains}
 
-        "posix";
-      };
 
       ${ cfg.extraConfig }
 
@@ -250,30 +487,32 @@ in
         '') cfg.virtualHosts) }
     '';
 
-    users.extraUsers.prosody = {
+    users.extraUsers.prosody = mkIf (cfg.user == "prosody") {
       uid = config.ids.uids.prosody;
       description = "Prosody user";
       createHome = true;
-      group = "prosody";
-      home = "/var/lib/prosody";
+      inherit (cfg) group;
+      home = "${cfg.dataDir}";
     };
 
-    users.extraGroups.prosody = {
+    users.extraGroups.prosody = mkIf (cfg.group == "prosody") {
       gid = config.ids.gids.prosody;
     };
 
     systemd.services.prosody = {
-
       description = "Prosody XMPP server";
       after = [ "network-online.target" ];
       wants = [ "network-online.target" ];
       wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ];
       serviceConfig = {
-        User = "prosody";
-        PIDFile = "/var/lib/prosody/prosody.pid";
-        ExecStart = "${pkgs.prosody}/bin/prosodyctl start";
+        User = cfg.user;
+        Group = cfg.group;
+        Type = "forking";
+        RuntimeDirectory = [ "prosody" ];
+        PIDFile = "/run/prosody/prosody.pid";
+        ExecStart = "${cfg.package}/bin/prosodyctl start";
       };
-
     };
 
   };
diff --git a/nixos/modules/services/networking/quagga.nix b/nixos/modules/services/networking/quagga.nix
index aab58cc77b90..22204e53203c 100644
--- a/nixos/modules/services/networking/quagga.nix
+++ b/nixos/modules/services/networking/quagga.nix
@@ -133,7 +133,7 @@ in
     users.groups = {
       quagga = {};
       # Members of the quaggavty group can use vtysh to inspect the Quagga daemons
-      quaggavty = {};
+      quaggavty = { members = [ "quagga" ]; };
     };
 
     systemd.services =
diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix
index 0199502163a3..85d7f9e4a41b 100644
--- a/nixos/modules/services/networking/radvd.nix
+++ b/nixos/modules/services/networking/radvd.nix
@@ -59,24 +59,11 @@ in
 
     systemd.services.radvd =
       { description = "IPv6 Router Advertisement Daemon";
-
         wantedBy = [ "multi-user.target" ];
-
         after = [ "network.target" ];
-
-        path = [ pkgs.radvd ];
-
-        preStart = ''
-          mkdir -m 755 -p /run/radvd
-          chown radvd /run/radvd
-        '';
-
         serviceConfig =
-          { ExecStart = "@${pkgs.radvd}/sbin/radvd radvd"
-              + " -p /run/radvd/radvd.pid -m syslog -u radvd -C ${confFile}";
+          { ExecStart = "@${pkgs.radvd}/bin/radvd radvd -n -u radvd -C ${confFile}";
             Restart = "always";
-            Type = "forking";
-            PIDFile = "/run/radvd/radvd.pid";
           };
       };
 
diff --git a/nixos/modules/services/networking/rdnssd.nix b/nixos/modules/services/networking/rdnssd.nix
index 95833d31e99d..a102242eae71 100644
--- a/nixos/modules/services/networking/rdnssd.nix
+++ b/nixos/modules/services/networking/rdnssd.nix
@@ -6,7 +6,7 @@
 with lib;
 let
   mergeHook = pkgs.writeScript "rdnssd-merge-hook" ''
-    #! ${pkgs.stdenv.shell} -e
+    #! ${pkgs.runtimeShell} -e
     ${pkgs.openresolv}/bin/resolvconf -u
   '';
 in
diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix
index 6d2b7bdbca1b..2956a5ecbc04 100644
--- a/nixos/modules/services/networking/resilio.nix
+++ b/nixos/modules/services/networking/resilio.nix
@@ -17,7 +17,7 @@ let
 
     search_lan = entry.searchLAN;
     use_sync_trash = entry.useSyncTrash;
-    known_hosts = knownHosts;
+    known_hosts = entry.knownHosts;
   }) cfg.sharedFolders;
 
   configFile = pkgs.writeText "config.json" (builtins.toJSON ({
@@ -50,12 +50,7 @@ in
         description = ''
           If enabled, start the Resilio Sync daemon. Once enabled, you can
           interact with the service through the Web UI, or configure it in your
-          NixOS configuration. Enabling the <literal>resilio</literal> service
-          also installs a systemd user unit which can be used to start
-          user-specific copies of the daemon. Once installed, you can use
-          <literal>systemctl --user start resilio</literal> as your user to start
-          the daemon using the configuration file located at
-          <literal>$HOME/.config/resilio-sync/config.json</literal>.
+          NixOS configuration.
         '';
       };
 
diff --git a/nixos/modules/services/networking/rxe.nix b/nixos/modules/services/networking/rxe.nix
new file mode 100644
index 000000000000..a6a069ec50c0
--- /dev/null
+++ b/nixos/modules/services/networking/rxe.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.rxe;
+
+  runRxeCmd = cmd: ifcs:
+    concatStrings ( map (x: "${pkgs.rdma-core}/bin/rxe_cfg -n ${cmd} ${x};") ifcs);
+
+  startScript = pkgs.writeShellScriptBin "rxe-start" ''
+    ${pkgs.rdma-core}/bin/rxe_cfg -n start
+    ${runRxeCmd "add" cfg.interfaces}
+    ${pkgs.rdma-core}/bin/rxe_cfg
+  '';
+
+  stopScript = pkgs.writeShellScriptBin "rxe-stop" ''
+    ${runRxeCmd "remove" cfg.interfaces }
+    ${pkgs.rdma-core}/bin/rxe_cfg -n stop
+  '';
+
+in {
+  ###### interface
+
+  options = {
+    networking.rxe = {
+      enable = mkEnableOption "RDMA over converged ethernet";
+      interfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "eth0" ];
+        description = ''
+          Enable RDMA on the listed interfaces. The corresponding virtual
+          RDMA interfaces will be named rxe0 ... rxeN where the ordering
+          will be as they are named in the list. UDP port 4791 must be
+          open on the respective ethernet interfaces.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.rxe = {
+      path = with pkgs; [ kmod rdma-core ];
+      description = "RoCE interfaces";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-modules-load.service" "network-online.target" ];
+      wants = [ "network-pre.target" ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "${startScript}/bin/rxe-start";
+        ExecStop = "${stopScript}/bin/rxe-stop";
+      };
+    };
+  };
+}
+
diff --git a/nixos/modules/services/networking/shadowsocks.nix b/nixos/modules/services/networking/shadowsocks.nix
new file mode 100644
index 000000000000..fe6d65a5f963
--- /dev/null
+++ b/nixos/modules/services/networking/shadowsocks.nix
@@ -0,0 +1,112 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.shadowsocks;
+
+  opts = {
+    server = cfg.localAddress;
+    server_port = cfg.port;
+    method = cfg.encryptionMethod;
+    mode = cfg.mode;
+    user = "nobody";
+    fast_open = true;
+  } // optionalAttrs (cfg.password != null) { password = cfg.password; };
+
+  configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.shadowsocks = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run shadowsocks-libev shadowsocks server.
+        '';
+      };
+
+      localAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          Local address to which the server binds.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 8388;
+        description = ''
+          Port which the server uses.
+        '';
+      };
+
+      password = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Password for connecting clients.
+        '';
+      };
+
+      passwordFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Password file with a password for connecting clients.
+        '';
+      };
+
+      mode = mkOption {
+        type = types.enum [ "tcp_only" "tcp_and_udp" "udp_only" ];
+        default = "tcp_and_udp";
+        description = ''
+          Relay protocols.
+        '';
+      };
+
+      encryptionMethod = mkOption {
+        type = types.str;
+        default = "chacha20-ietf-poly1305";
+        description = ''
+          Encryption method. See <link xlink:href="https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers"/>.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    assertions = singleton
+      { assertion = cfg.password == null || cfg.passwordFile == null;
+        message = "Cannot use both password and passwordFile for shadowsocks-libev";
+      };
+
+    systemd.services.shadowsocks-libev = {
+      description = "shadowsocks-libev Daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq;
+      serviceConfig.PrivateTmp = true;
+      script = ''
+        ${optionalString (cfg.passwordFile != null) ''
+          cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json
+        ''}
+        exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/softether.nix b/nixos/modules/services/networking/softether.nix
index 9087b75c29c1..65df93a00da9 100644
--- a/nixos/modules/services/networking/softether.nix
+++ b/nixos/modules/services/networking/softether.nix
@@ -5,6 +5,8 @@ with lib;
 let
   cfg = config.services.softether;
 
+  package = cfg.package.override { dataDir = cfg.dataDir; };
+
 in
 {
 
@@ -49,7 +51,7 @@ in
 
       dataDir = mkOption {
         type = types.string;
-        default = "${cfg.package.dataDir}";
+        default = "/var/lib/softether";
         description = ''
           Data directory for SoftEther VPN.
         '';
@@ -64,11 +66,8 @@ in
   config = mkIf cfg.enable (
 
     mkMerge [{
-      environment.systemPackages = [
-          (pkgs.lib.overrideDerivation cfg.package (attrs: {
-            dataDir = cfg.dataDir;
-          }))
-        ];
+      environment.systemPackages = [ package ];
+
       systemd.services."softether-init" = {
         description = "SoftEther VPN services initial task";
         wantedBy = [ "network.target" ];
@@ -80,11 +79,11 @@ in
             for d in vpnserver vpnbridge vpnclient vpncmd; do
                 if ! test -e ${cfg.dataDir}/$d; then
                     ${pkgs.coreutils}/bin/mkdir -m0700 -p ${cfg.dataDir}/$d
-                    install -m0600 ${cfg.package}${cfg.dataDir}/$d/hamcore.se2 ${cfg.dataDir}/$d/hamcore.se2
+                    install -m0600 ${package}${cfg.dataDir}/$d/hamcore.se2 ${cfg.dataDir}/$d/hamcore.se2
                 fi
             done
             rm -rf ${cfg.dataDir}/vpncmd/vpncmd
-            ln -s ${cfg.package}${cfg.dataDir}/vpncmd/vpncmd ${cfg.dataDir}/vpncmd/vpncmd
+            ln -s ${package}${cfg.dataDir}/vpncmd/vpncmd ${cfg.dataDir}/vpncmd/vpncmd
         '';
       };
     }
@@ -97,12 +96,12 @@ in
         wantedBy = [ "network.target" ];
         serviceConfig = {
           Type = "forking";
-          ExecStart = "${cfg.package}/bin/vpnserver start";
-          ExecStop = "${cfg.package}/bin/vpnserver stop";
+          ExecStart = "${package}/bin/vpnserver start";
+          ExecStop = "${package}/bin/vpnserver stop";
         };
         preStart = ''
             rm -rf ${cfg.dataDir}/vpnserver/vpnserver
-            ln -s ${cfg.package}${cfg.dataDir}/vpnserver/vpnserver ${cfg.dataDir}/vpnserver/vpnserver
+            ln -s ${package}${cfg.dataDir}/vpnserver/vpnserver ${cfg.dataDir}/vpnserver/vpnserver
         '';
         postStop = ''
             rm -rf ${cfg.dataDir}/vpnserver/vpnserver
@@ -118,12 +117,12 @@ in
         wantedBy = [ "network.target" ];
         serviceConfig = {
           Type = "forking";
-          ExecStart = "${cfg.package}/bin/vpnbridge start";
-          ExecStop = "${cfg.package}/bin/vpnbridge stop";
+          ExecStart = "${package}/bin/vpnbridge start";
+          ExecStop = "${package}/bin/vpnbridge stop";
         };
         preStart = ''
             rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge
-            ln -s ${cfg.package}${cfg.dataDir}/vpnbridge/vpnbridge ${cfg.dataDir}/vpnbridge/vpnbridge
+            ln -s ${package}${cfg.dataDir}/vpnbridge/vpnbridge ${cfg.dataDir}/vpnbridge/vpnbridge
         '';
         postStop = ''
             rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge
@@ -139,12 +138,12 @@ in
         wantedBy = [ "network.target" ];
         serviceConfig = {
           Type = "forking";
-          ExecStart = "${cfg.package}/bin/vpnclient start";
-          ExecStop = "${cfg.package}/bin/vpnclient stop";
+          ExecStart = "${package}/bin/vpnclient start";
+          ExecStop = "${package}/bin/vpnclient stop";
         };
         preStart = ''
             rm -rf ${cfg.dataDir}/vpnclient/vpnclient
-            ln -s ${cfg.package}${cfg.dataDir}/vpnclient/vpnclient ${cfg.dataDir}/vpnclient/vpnclient
+            ln -s ${package}${cfg.dataDir}/vpnclient/vpnclient ${cfg.dataDir}/vpnclient/vpnclient
         '';
         postStart = ''
             sleep 1
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 8828429a8178..aab1203086ce 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -21,7 +21,7 @@ let
           daemon reads in addition to the the user's authorized_keys file.
           You can combine the <literal>keys</literal> and
           <literal>keyFiles</literal> options.
-          Warning: If you are using <literal>NixOps</literal> then don't use this 
+          Warning: If you are using <literal>NixOps</literal> then don't use this
           option since it will replace the key required for deployment via ssh.
         '';
       };
@@ -54,8 +54,6 @@ let
     ));
   in listToAttrs (map mkAuthKeyFile usersWithKeys);
 
-  supportOldHostKeys = !versionAtLeast config.system.stateVersion "15.07";
-
 in
 
 {
@@ -139,6 +137,14 @@ in
         '';
       };
 
+      openFirewall = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to automatically open the specified ports in the firewall.
+        '';
+      };
+
       listenAddresses = mkOption {
         type = with types; listOf (submodule {
           options = {
@@ -191,9 +197,6 @@ in
         default =
           [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; }
             { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
-          ] ++ optionals supportOldHostKeys
-          [ { type = "dsa"; path = "/etc/ssh/ssh_host_dsa_key"; }
-            { type = "ecdsa"; bits = 521; path = "/etc/ssh/ssh_host_ecdsa_key"; }
           ];
         description = ''
           NixOS can automatically generate SSH host keys.  This option
@@ -210,6 +213,65 @@ in
         description = "Files from which authorized keys are read.";
       };
 
+      kexAlgorithms = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "curve25519-sha256@libssh.org"
+          "diffie-hellman-group-exchange-sha256"
+        ];
+        description = ''
+          Allowed key exchange algorithms
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
+      ciphers = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "chacha20-poly1305@openssh.com"
+          "aes256-gcm@openssh.com"
+          "aes128-gcm@openssh.com"
+          "aes256-ctr"
+          "aes192-ctr"
+          "aes128-ctr"
+        ];
+        description = ''
+          Allowed ciphers
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
+      macs = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "hmac-sha2-512-etm@openssh.com"
+          "hmac-sha2-256-etm@openssh.com"
+          "umac-128-etm@openssh.com"
+          "hmac-sha2-512"
+          "hmac-sha2-256"
+          "umac-128@openssh.com"
+        ];
+        description = ''
+          Allowed MACs
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -253,13 +315,10 @@ in
       let
         service =
           { description = "SSH Daemon";
-
             wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
-
+            after = [ "network.target" ];
             stopIfChanged = false;
-
             path = [ cfgc.package pkgs.gawk ];
-
             environment.LD_LIBRARY_PATH = nssModulesPath;
 
             preStart =
@@ -310,7 +369,7 @@ in
 
       };
 
-    networking.firewall.allowedTCPPorts = cfg.ports;
+    networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else [];
 
     security.pam.services.sshd =
       { startSession = true;
@@ -363,14 +422,13 @@ in
           HostKey ${k.path}
         '')}
 
-        # Allow DSA client keys for now. (These were deprecated
-        # in OpenSSH 7.0.)
-        PubkeyAcceptedKeyTypes +ssh-dss
+        KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}
+        Ciphers ${concatStringsSep "," cfg.ciphers}
+        MACs ${concatStringsSep "," cfg.macs}
 
-        # Re-enable DSA host keys for now.
-        ${optionalString supportOldHostKeys ''
-          HostKeyAlgorithms +ssh-dss
-        ''}
+        # LogLevel VERBOSE logs user's key fingerprint on login.
+        # Needed to have a clear audit track of which key was used to log in.
+        LogLevel VERBOSE
       '';
 
     assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
diff --git a/nixos/modules/services/networking/strongswan.nix b/nixos/modules/services/networking/strongswan.nix
index 3a3f64221c42..707d24b9220f 100644
--- a/nixos/modules/services/networking/strongswan.nix
+++ b/nixos/modules/services/networking/strongswan.nix
@@ -32,13 +32,13 @@ let
       ${caConf}
     '';
 
-  strongswanConf = {setup, connections, ca, secrets, managePlugins, enabledPlugins}: toFile "strongswan.conf" ''
+  strongswanConf = {setup, connections, ca, secretsFile, managePlugins, enabledPlugins}: toFile "strongswan.conf" ''
     charon {
       ${if managePlugins then "load_modular = no" else ""}
       ${if managePlugins then ("load = " + (concatStringsSep " " enabledPlugins)) else ""}
       plugins {
         stroke {
-          secrets_file = ${ipsecSecrets secrets}
+          secrets_file = ${secretsFile}
         }
       }
     }
@@ -135,7 +135,18 @@ in
     };
   };
 
-  config = with cfg; mkIf enable {
+
+  config = with cfg;
+  let
+    secretsFile = ipsecSecrets cfg.secrets;
+  in
+  mkIf enable
+    {
+
+    # here we should use the default strongswan ipsec.secrets and
+    # append to it (default one is empty so not a pb for now)
+    environment.etc."ipsec.secrets".source = secretsFile;
+
     systemd.services.strongswan = {
       description = "strongSwan IPSec Service";
       wantedBy = [ "multi-user.target" ];
@@ -143,11 +154,15 @@ in
       wants = [ "keys.target" ];
       after = [ "network-online.target" "keys.target" ];
       environment = {
-        STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secrets managePlugins enabledPlugins; };
+        STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; };
       };
       serviceConfig = {
         ExecStart  = "${pkgs.strongswan}/sbin/ipsec start --nofork";
       };
+      preStart = ''
+        # with 'nopeerdns' setting, ppp writes into this folder
+        mkdir -m 700 -p /etc/ppp
+      '';
     };
   };
 }
diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix
new file mode 100644
index 000000000000..89a14966eca7
--- /dev/null
+++ b/nixos/modules/services/networking/stunnel.nix
@@ -0,0 +1,221 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.stunnel;
+  yesNo = val: if val then "yes" else "no";
+
+  verifyChainPathAssert = n: c: {
+    assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer);
+    message =  "stunnel: \"${n}\" client configuration - hostname verification " +
+      "is not possible without either verifyChain or verifyPeer enabled";
+  };
+
+  serverConfig = {
+    options = {
+      accept = mkOption {
+        type = types.int;
+        description = "On which port stunnel should listen for incoming TLS connections.";
+      };
+
+      connect = mkOption {
+        type = types.int;
+        description = "To which port the decrypted connection should be forwarded.";
+      };
+
+      cert = mkOption {
+        type = types.path;
+        description = "File containing both the private and public keys.";
+      };
+    };
+  };
+
+  clientConfig = {
+    options = {
+      accept = mkOption {
+        type = types.string;
+        description = "IP:Port on which connections should be accepted.";
+      };
+
+      connect = mkOption {
+        type = types.string;
+        description = "IP:Port destination to connect to.";
+      };
+
+      verifyChain = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Check if the provided certificate has a valid certificate chain (against CAPath).";
+      };
+
+      verifyPeer = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Check if the provided certificate is contained in CAPath.";
+      };
+
+      CAPath = mkOption {
+        type = types.path;
+        default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+        description = "Path to a file containing certificates to validate against.";
+      };
+
+      verifyHostname = mkOption {
+        type = with types; nullOr string;
+        default = null;
+        description = "If set, stunnel checks if the provided certificate is valid for the given hostname.";
+      };
+    };
+  };
+
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.stunnel = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the stunnel TLS tunneling service.";
+      };
+
+      user = mkOption {
+        type = with types; nullOr string;
+        default = "nobody";
+        description = "The user under which stunnel runs.";
+      };
+
+      group = mkOption {
+        type = with types; nullOr string;
+        default = "nogroup";
+        description = "The group under which stunnel runs.";
+      };
+
+      logLevel = mkOption {
+        type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ];
+        default = "info";
+        description = "Verbosity of stunnel output.";
+      };
+
+      fipsMode = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable FIPS 140-2 mode required for compliance.";
+      };
+
+      enableInsecureSSLv3 = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable support for the insecure SSLv3 protocol.";
+      };
+
+
+      servers = mkOption {
+        description = "Define the server configuations.";
+        type = with types; attrsOf (submodule serverConfig);
+        example = {
+          fancyWebserver = {
+            enable = true;
+            accept = 443;
+            connect = 8080;
+            cert = "/path/to/pem/file";
+          };
+        };
+        default = { };
+      };
+
+      clients = mkOption {
+        description = "Define the client configurations.";
+        type = with types; attrsOf (submodule clientConfig);
+        example = {
+          foobar = {
+            accept = "0.0.0.0:8080";
+            connect = "nixos.org:443";
+            verifyChain = false;
+          };
+        };
+        default = { };
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = concatLists [
+      (singleton {
+        assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0);
+        message = "stunnel: At least one server- or client-configuration has to be present.";
+      })
+
+      (mapAttrsToList verifyChainPathAssert cfg.clients)
+    ];
+
+    environment.systemPackages = [ pkgs.stunnel ];
+
+    environment.etc."stunnel.cfg".text = ''
+      ${ if cfg.user != null then "setuid = ${cfg.user}" else "" }
+      ${ if cfg.group != null then "setgid = ${cfg.group}" else "" }
+
+      debug = ${cfg.logLevel}
+
+      ${ optionalString cfg.fipsMode "fips = yes" }
+      ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
+
+      ; ----- SERVER CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               accept = ${toString v.accept}
+               connect = ${toString v.connect}
+               cert = ${v.cert}
+
+             '')
+           cfg.servers)
+      }
+
+      ; ----- CLIENT CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               client = yes
+               accept = ${v.accept}
+               connect = ${v.connect}
+               verifyChain = ${yesNo v.verifyChain}
+               verifyPeer = ${yesNo v.verifyPeer}
+               ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"}
+               ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
+               OCSPaia = yes
+
+             '')
+           cfg.clients)
+      }
+    '';
+
+    systemd.services.stunnel = {
+      description = "stunnel TLS tunneling service";
+      after = [ "network.target" ];
+      wants = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ config.environment.etc."stunnel.cfg".source ];
+      serviceConfig = {
+        ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}";
+        Type = "forking";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index dcdc203bdc62..e485c073cbdd 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -16,12 +16,6 @@ in {
         available on http://127.0.0.1:8384/.
       '';
 
-      useInotify = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Provide syncthing-inotify as a service.";
-      };
-
       systemService = mkOption {
         type = types.bool;
         default = true;
@@ -90,6 +84,12 @@ in {
     };
   };
 
+  imports = [
+    (mkRemovedOptionModule ["services" "syncthing" "useInotify"] ''
+      This option was removed because syncthing now has the inotify functionality included under the name "fswatcher".
+      It can be enabled on a per-folder basis through the webinterface.
+    '')
+  ];
 
   ###### implementation
 
@@ -100,8 +100,7 @@ in {
       allowedUDPPorts = [ 21027 ];
     };
 
-    systemd.packages = [ pkgs.syncthing ]
-                       ++ lib.optional cfg.useInotify pkgs.syncthing-inotify;
+    systemd.packages = [ pkgs.syncthing ];
 
     users = mkIf (cfg.user == defaultUser) {
       extraUsers."${defaultUser}" =
@@ -125,7 +124,6 @@ in {
           STNOUPGRADE = "yes";
           inherit (cfg) all_proxy;
         } // config.networking.proxy.envVars;
-        wants = mkIf cfg.useInotify [ "syncthing-inotify.service" ];
         wantedBy = [ "multi-user.target" ];
         serviceConfig = {
           Restart = "on-failure";
@@ -141,20 +139,6 @@ in {
       syncthing-resume = {
         wantedBy = [ "suspend.target" ];
       };
-
-      syncthing-inotify = mkIf (cfg.systemService && cfg.useInotify) {
-        description = "Syncthing Inotify File Watcher service";
-        after = [ "network.target" "syncthing.service" ];
-        requires = [ "syncthing.service" ];
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig = {
-          SuccessExitStatus = "2";
-          RestartForceExitStatus = "3";
-          Restart = "on-failure";
-          User = cfg.user;
-          ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -home=${cfg.dataDir} -logflags=0";
-        };
-      };
     };
   };
 }
diff --git a/nixos/modules/services/networking/tcpcrypt.nix b/nixos/modules/services/networking/tcpcrypt.nix
index 2f304165eb4b..ee005e11aa32 100644
--- a/nixos/modules/services/networking/tcpcrypt.nix
+++ b/nixos/modules/services/networking/tcpcrypt.nix
@@ -44,9 +44,9 @@ in
       path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ];
 
       preStart = ''
-        mkdir -p /var/run/tcpcryptd
-        chown tcpcryptd /var/run/tcpcryptd
-        sysctl -n net.ipv4.tcp_ecn >/run/pre-tcpcrypt-ecn-state
+        mkdir -p /run/tcpcryptd
+        chown tcpcryptd /run/tcpcryptd
+        sysctl -n net.ipv4.tcp_ecn > /run/tcpcryptd/pre-tcpcrypt-ecn-state
         sysctl -w net.ipv4.tcp_ecn=0
 
         iptables -t raw -N nixos-tcpcrypt
@@ -61,8 +61,8 @@ in
       script = "tcpcryptd -x 0x10";
 
       postStop = ''
-        if [ -f /run/pre-tcpcrypt-ecn-state ]; then
-          sysctl -w net.ipv4.tcp_ecn=$(cat /run/pre-tcpcrypt-ecn-state)
+        if [ -f /run/tcpcryptd/pre-tcpcrypt-ecn-state ]; then
+          sysctl -w net.ipv4.tcp_ecn=$(cat /run/tcpcryptd/pre-tcpcrypt-ecn-state)
         fi
 
         iptables -t mangle -D POSTROUTING -j nixos-tcpcrypt || true
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index adb80ea29840..e3c9b5282b8c 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -178,6 +178,8 @@ in
         preStart = ''
           mkdir -p /etc/tinc/${network}/hosts
           chown tinc.${network} /etc/tinc/${network}/hosts
+          mkdir -p /etc/tinc/${network}/invitations
+          chown tinc.${network} /etc/tinc/${network}/invitations
 
           # Determine how we should generate our keys
           if type tinc >/dev/null 2>&1; then
diff --git a/nixos/modules/services/networking/tox-bootstrapd.nix b/nixos/modules/services/networking/tox-bootstrapd.nix
index c1f945773e23..cb0e6b158651 100644
--- a/nixos/modules/services/networking/tox-bootstrapd.nix
+++ b/nixos/modules/services/networking/tox-bootstrapd.nix
@@ -69,7 +69,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig =
-        { ExecStart = "${pkg}/bin/tox-bootstrapd ${cfgFile}";
+        { ExecStart = "${pkg}/bin/tox-bootstrapd --config=${cfgFile}";
           Type = "forking";
           inherit PIDFile;
           User = "tox-bootstrapd";
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index 545ee327d596..f069a9883a7f 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -112,7 +112,7 @@ in
         mkdir -m 0755 -p ${stateDir}/dev/
         cp ${confFile} ${stateDir}/unbound.conf
         ${optionalString cfg.enableRootTrustAnchor ''
-        ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile}
+        ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
         chown unbound ${stateDir} ${rootTrustAnchorFile}
         ''}
         touch ${stateDir}/dev/random
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 86e0204ec2f7..cd1617b8e2ba 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -7,6 +7,16 @@ let
 in
 {
   options.services.zerotierone.enable = mkEnableOption "ZeroTierOne";
+
+  options.services.zerotierone.joinNetworks = mkOption {
+    default = [];
+    example = [ "a8a2c3c10c1a68de" ];
+    type = types.listOf types.str;
+    description = ''
+      List of ZeroTier Network IDs to join on startup
+    '';
+  };
+
   options.services.zerotierone.package = mkOption {
     default = pkgs.zerotierone;
     defaultText = "pkgs.zerotierone";
@@ -22,12 +32,13 @@ in
       path = [ cfg.package ];
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      preStart =
-        ''
-        mkdir -p /var/lib/zerotier-one
+      preStart = ''
+        mkdir -p /var/lib/zerotier-one/networks.d
         chmod 700 /var/lib/zerotier-one
         chown -R root:root /var/lib/zerotier-one
-        '';
+      '' + (concatMapStrings (netId: ''
+        touch "/var/lib/zerotier-one/networks.d/${netId}.conf"
+      '') cfg.joinNetworks);
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/zerotier-one";
         Restart = "always";
@@ -38,6 +49,9 @@ in
     # ZeroTier does not issue DHCP leases, but some strangers might...
     networking.dhcpcd.denyInterfaces = [ "zt0" ];
 
+    # ZeroTier receives UDP transmissions on port 9993 by default
+    networking.firewall.allowedUDPPorts = [ 9993 ];
+
     environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 4c7f58d1d8bc..ecab8cfc7df9 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -124,7 +124,7 @@ in
 
       listenAddresses = mkOption {
         type = types.listOf types.str;
-        default = [ "127.0.0.1:631" ];
+        default = [ "localhost:631" ];
         example = [ "*:631" ];
         description = ''
           A list of addresses and ports on which to listen.
@@ -321,7 +321,10 @@ in
             ''}
           '';
 
-          serviceConfig.PrivateTmp = true;
+          serviceConfig = {
+            PrivateTmp = true;
+            RuntimeDirectory = [ "cups" ];
+          };
       };
 
     systemd.services.cups-browsed = mkIf avahiEnabled
diff --git a/nixos/modules/services/scheduling/fcron.nix b/nixos/modules/services/scheduling/fcron.nix
index ac589be57736..e3b6b638f5a7 100644
--- a/nixos/modules/services/scheduling/fcron.nix
+++ b/nixos/modules/services/scheduling/fcron.nix
@@ -90,16 +90,24 @@ in
       [ (allowdeny "allow" (cfg.allow))
         (allowdeny "deny" cfg.deny)
         # see man 5 fcron.conf
-        { source = pkgs.writeText "fcron.conf" ''
-            fcrontabs   =       /var/spool/fcron
-            pidfile     =       /var/run/fcron.pid
-            fifofile    =       /var/run/fcron.fifo
-            fcronallow  =       /etc/fcron.allow
-            fcrondeny   =       /etc/fcron.deny
-            shell       =       /bin/sh
-            sendmail    =       /run/wrappers/bin/sendmail
-            editor      =       ${pkgs.vim}/bin/vim
-          '';
+        { source =
+            let
+              isSendmailWrapped =
+                lib.hasAttr "sendmail" config.security.wrappers;
+              sendmailPath =
+                if isSendmailWrapped then "/run/wrappers/bin/sendmail"
+                else "${config.system.path}/bin/sendmail";
+            in
+            pkgs.writeText "fcron.conf" ''
+              fcrontabs   =       /var/spool/fcron
+              pidfile     =       /var/run/fcron.pid
+              fifofile    =       /var/run/fcron.fifo
+              fcronallow  =       /etc/fcron.allow
+              fcrondeny   =       /etc/fcron.deny
+              shell       =       /bin/sh
+              sendmail    =       ${sendmailPath}
+              editor      =       ${pkgs.vim}/bin/vim
+            '';
           target = "fcron.conf";
           gid = config.ids.gids.fcron;
           mode = "0644";
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
index c51dd5d94655..d61f588205af 100644
--- a/nixos/modules/services/search/elasticsearch.nix
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -6,6 +6,7 @@ let
   cfg = config.services.elasticsearch;
 
   es5 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "5" >= 0;
+  es6 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "6" >= 0;
 
   esConfig = ''
     network.host: ${cfg.listenAddress}
@@ -31,8 +32,11 @@ let
       (if es5 then (pkgs.writeTextDir "log4j2.properties" cfg.logging)
               else (pkgs.writeTextDir "logging.yml" cfg.logging))
     ];
-    # Elasticsearch 5.x won't start when the scripts directory does not exist
-    postBuild = if es5 then "${pkgs.coreutils}/bin/mkdir -p $out/scripts" else "";
+    postBuild = concatStringsSep "\n" (concatLists [
+      # Elasticsearch 5.x won't start when the scripts directory does not exist
+      (optional es5 "${pkgs.coreutils}/bin/mkdir -p $out/scripts")
+      (optional es6 "ln -s ${cfg.package}/config/jvm.options $out/jvm.options")
+    ]);
   };
 
   esPlugins = pkgs.buildEnv {
@@ -92,8 +96,6 @@ in {
         node.name: "elasticsearch"
         node.master: true
         node.data: false
-        index.number_of_shards: 5
-        index.number_of_replicas: 1
       '';
     };
 
@@ -165,7 +167,10 @@ in {
       path = [ pkgs.inetutils ];
       environment = {
         ES_HOME = cfg.dataDir;
-        ES_JAVA_OPTS = toString ([ "-Des.path.conf=${configDir}" ] ++ cfg.extraJavaOptions);
+        ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ]
+                                  ++ cfg.extraJavaOptions);
+      } // optionalAttrs es6 {
+        ES_PATH_CONF = configDir;
       };
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}";
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index f71f30fee97a..4161c61ed375 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -76,8 +76,9 @@ in
     };
   };
 
-  config = mkIf cfg.updater.enable or cfg.daemon.enable {
+  config = mkIf (cfg.updater.enable || cfg.daemon.enable) {
     environment.systemPackages = [ pkg ];
+
     users.extraUsers = singleton {
       name = clamavUser;
       uid = config.ids.uids.clamav;
@@ -94,10 +95,10 @@ in
     environment.etc."clamav/freshclam.conf".source = freshclamConfigFile;
     environment.etc."clamav/clamd.conf".source = clamdConfigFile;
 
-    systemd.services.clamav-daemon = mkIf cfg.daemon.enable {
+    systemd.services.clamav-daemon = optionalAttrs cfg.daemon.enable {
       description = "ClamAV daemon (clamd)";
-      after = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
-      requires = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
+      after = optional cfg.updater.enable "clamav-freshclam.service";
+      requires = optional cfg.updater.enable "clamav-freshclam.service";
       wantedBy = [ "multi-user.target" ];
       restartTriggers = [ clamdConfigFile ];
 
@@ -115,7 +116,7 @@ in
       };
     };
 
-    systemd.timers.clamav-freshclam = mkIf cfg.updater.enable {
+    systemd.timers.clamav-freshclam = optionalAttrs cfg.updater.enable {
       description = "Timer for ClamAV virus database updater (freshclam)";
       wantedBy = [ "timers.target" ];
       timerConfig = {
@@ -124,7 +125,7 @@ in
       };
     };
 
-    systemd.services.clamav-freshclam = mkIf cfg.updater.enable {
+    systemd.services.clamav-freshclam = optionalAttrs cfg.updater.enable {
       description = "ClamAV virus database updater (freshclam)";
       restartTriggers = [ freshclamConfigFile ];
 
diff --git a/nixos/modules/services/security/hologram-agent.nix b/nixos/modules/services/security/hologram-agent.nix
index 6c53a2df6306..39ed506f7617 100644
--- a/nixos/modules/services/security/hologram-agent.nix
+++ b/nixos/modules/services/security/hologram-agent.nix
@@ -35,10 +35,9 @@ in {
   config = mkIf cfg.enable {
     boot.kernelModules = [ "dummy" ];
 
-    networking.interfaces.dummy0 = {
-      ipAddress = "169.254.169.254";
-      prefixLength = 32;
-    };
+    networking.interfaces.dummy0.ipv4.addresses = [
+      { address = "169.254.169.254"; prefixLength = 32; }
+    ];
 
     systemd.services.hologram-agent = {
       description = "Provide EC2 instance credentials to machines outside of EC2";
diff --git a/nixos/modules/services/security/hologram-server.nix b/nixos/modules/services/security/hologram-server.nix
index e267fed27955..bad02c7440ba 100644
--- a/nixos/modules/services/security/hologram-server.nix
+++ b/nixos/modules/services/security/hologram-server.nix
@@ -12,16 +12,20 @@ let
         dn       = cfg.ldapBindDN;
         password = cfg.ldapBindPassword;
       };
-      insecureldap = cfg.ldapInsecure;
-      userattr     = cfg.ldapUserAttr;
-      baseDN       = cfg.ldapBaseDN;
+      insecureldap    = cfg.ldapInsecure;
+      userattr        = cfg.ldapUserAttr;
+      baseDN          = cfg.ldapBaseDN;
+      enableldapRoles = cfg.enableLdapRoles;
+      roleAttr        = cfg.roleAttr;
+      groupClassAttr  = cfg.groupClassAttr;
     };
     aws = {
       account     = cfg.awsAccount;
       defaultrole = cfg.awsDefaultRole;
     };
-    stats  = cfg.statsAddress;
-    listen = cfg.listenAddress;
+    stats        = cfg.statsAddress;
+    listen       = cfg.listenAddress;
+    cachetimeout = cfg.cacheTimeoutSeconds;
   });
 in {
   options = {
@@ -70,6 +74,24 @@ in {
         description = "Password of account to use to query the LDAP server";
       };
 
+      enableLdapRoles = mkOption {
+        type        = types.bool;
+        default     = false;
+        description = "Whether to assign user roles based on the user's LDAP group memberships";
+      };
+
+      groupClassAttr = mkOption {
+        type = types.str;
+        default = "groupOfNames";
+        description = "The objectclass attribute to search for groups when enableLdapRoles is true";
+      };
+
+      roleAttr = mkOption {
+        type        = types.str;
+        default     = "businessCategory";
+        description = "Which LDAP group attribute to search for authorized role ARNs";
+      };
+
       awsAccount = mkOption {
         type        = types.str;
         description = "AWS account number";
@@ -85,6 +107,12 @@ in {
         default     = "";
         description = "Address of statsd server";
       };
+
+      cacheTimeoutSeconds = mkOption {
+        type        = types.int;
+        default     = 3600;
+        description = "How often (in seconds) to refresh the LDAP cache";
+      };
     };
   };
 
diff --git a/nixos/modules/services/security/physlock.nix b/nixos/modules/services/security/physlock.nix
index 30224d7fc6ba..97fbd6aae6e0 100644
--- a/nixos/modules/services/security/physlock.nix
+++ b/nixos/modules/services/security/physlock.nix
@@ -30,6 +30,20 @@ in
         '';
       };
 
+      allowAnyUser = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to allow any user to lock the screen. This will install a
+          setuid wrapper to allow any user to start physlock as root, which
+          is a minor security risk. Call the physlock binary to use this instead
+          of using the systemd service.
+
+          Note that you might need to relog to have the correct binary in your
+          PATH upon changing this option.
+        '';
+      };
+
       disableSysRq = mkOption {
         type = types.bool;
         default = true;
@@ -79,28 +93,36 @@ in
 
   ###### implementation
 
-  config = mkIf cfg.enable {
-
-    # for physlock -l and physlock -L
-    environment.systemPackages = [ pkgs.physlock ];
-
-    systemd.services."physlock" = {
-      enable = true;
-      description = "Physlock";
-      wantedBy = optional cfg.lockOn.suspend   "suspend.target"
-              ++ optional cfg.lockOn.hibernate "hibernate.target"
-              ++ cfg.lockOn.extraTargets;
-      before   = optional cfg.lockOn.suspend   "systemd-suspend.service"
-              ++ optional cfg.lockOn.hibernate "systemd-hibernate.service"
-              ++ cfg.lockOn.extraTargets;
-      serviceConfig.Type = "forking";
-      script = ''
-        ${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}
-      '';
-    };
+  config = mkIf cfg.enable (mkMerge [
+    {
+
+      # for physlock -l and physlock -L
+      environment.systemPackages = [ pkgs.physlock ];
+
+      systemd.services."physlock" = {
+        enable = true;
+        description = "Physlock";
+        wantedBy = optional cfg.lockOn.suspend   "suspend.target"
+                ++ optional cfg.lockOn.hibernate "hibernate.target"
+                ++ cfg.lockOn.extraTargets;
+        before   = optional cfg.lockOn.suspend   "systemd-suspend.service"
+                ++ optional cfg.lockOn.hibernate "systemd-hibernate.service"
+                ++ cfg.lockOn.extraTargets;
+        serviceConfig = {
+          Type = "forking";
+          ExecStart = "${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}";
+        };
+      };
 
-    security.pam.services.physlock = {};
+      security.pam.services.physlock = {};
 
-  };
+    }
+
+    (mkIf cfg.allowAnyUser {
+
+      security.wrappers.physlock = { source = "${pkgs.physlock}/bin/physlock"; user = "root"; };
+
+    })
+  ]);
 
 }
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index bc79d9f2a590..806252f49b8d 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -5,10 +5,31 @@ with lib;
 let
   cfg = config.services.tor;
   torDirectory = "/var/lib/tor";
+  torRunDirectory = "/run/tor";
 
   opt    = name: value: optionalString (value != null) "${name} ${value}";
   optint = name: value: optionalString (value != null && value != 0)    "${name} ${toString value}";
 
+  isolationOptions = {
+    type = types.listOf (types.enum [
+      "IsolateClientAddr"
+      "IsolateSOCKSAuth"
+      "IsolateClientProtocol"
+      "IsolateDestPort"
+      "IsolateDestAddr"
+    ]);
+    default = [];
+    example = [
+      "IsolateClientAddr"
+      "IsolateSOCKSAuth"
+      "IsolateClientProtocol"
+      "IsolateDestPort"
+      "IsolateDestAddr"
+    ];
+    description = "Tor isolation options";
+  };
+
+
   torRc = ''
     User tor
     DataDirectory ${torDirectory}
@@ -18,12 +39,23 @@ let
     ''}
 
     ${optint "ControlPort" cfg.controlPort}
+    ${optionalString cfg.controlSocket.enable "ControlSocket ${torRunDirectory}/control GroupWritable RelaxDirModeCheck"}
   ''
   # Client connection config
-  + optionalString cfg.client.enable  ''
-    SOCKSPort ${cfg.client.socksListenAddress} IsolateDestAddr
+  + optionalString cfg.client.enable ''
+    SOCKSPort ${cfg.client.socksListenAddress} ${toString cfg.client.socksIsolationOptions}
     SOCKSPort ${cfg.client.socksListenAddressFaster}
     ${opt "SocksPolicy" cfg.client.socksPolicy}
+
+    ${optionalString cfg.client.transparentProxy.enable ''
+    TransPort ${cfg.client.transparentProxy.listenAddress} ${toString cfg.client.transparentProxy.isolationOptions}
+    ''}
+
+    ${optionalString cfg.client.dns.enable ''
+    DNSPort ${cfg.client.dns.listenAddress} ${toString cfg.client.dns.isolationOptions}
+    AutomapHostsOnResolve 1
+    AutomapHostsSuffixes ${concatStringsSep "," cfg.client.dns.automapHostsSuffixes}
+    ''}
   ''
   # Relay config
   + optionalString cfg.relay.enable ''
@@ -58,6 +90,9 @@ let
     ${flip concatMapStrings v.map (p: ''
       HiddenServicePort ${toString p.port} ${p.destination}
     '')}
+    ${optionalString (v.authorizeClient != null) ''
+      HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames}
+    ''}
   ''))
   + cfg.extraConfig;
 
@@ -107,6 +142,17 @@ in
         '';
       };
 
+      controlSocket = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Wheter to enable Tor control socket. Control socket is created
+            in <literal>${torRunDirectory}/control</literal>
+          '';
+        };
+      };
+
       client = {
         enable = mkOption {
           type = types.bool;
@@ -154,6 +200,55 @@ in
           '';
         };
 
+        socksIsolationOptions = mkOption (isolationOptions // {
+          default = ["IsolateDestAddr"];
+        });
+
+        transparentProxy = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable tor transaprent proxy";
+          };
+
+          listenAddress = mkOption {
+            type = types.str;
+            default = "127.0.0.1:9040";
+            example = "192.168.0.1:9040";
+            description = ''
+              Bind transparent proxy to this address.
+            '';
+          };
+
+          isolationOptions = mkOption isolationOptions;
+        };
+
+        dns = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable tor dns resolver";
+          };
+
+          listenAddress = mkOption {
+            type = types.str;
+            default = "127.0.0.1:9053";
+            example = "192.168.0.1:9053";
+            description = ''
+              Bind tor dns to this address.
+            '';
+          };
+
+          isolationOptions = mkOption isolationOptions;
+
+          automapHostsSuffixes = mkOption {
+            type = types.listOf types.str;
+            default = [".onion" ".exit"];
+            example = [".onion"];
+            description = "List of suffixes to use with automapHostsOnResolve";
+          };
+        };
+
         privoxy.enable = mkOption {
           type = types.bool;
           default = true;
@@ -540,6 +635,33 @@ in
                }));
              };
 
+             authorizeClient = mkOption {
+               default = null;
+               description = "If configured, the hidden service is accessible for authorized clients only.";
+               type = types.nullOr (types.submodule ({config, ...}: {
+
+                 options = {
+
+                   authType = mkOption {
+                     type = types.enum [ "basic" "stealth" ];
+                     description = ''
+                       Either <literal>"basic"</literal> for a general-purpose authorization protocol
+                       or <literal>"stealth"</literal> for a less scalable protocol
+                       that also hides service activity from unauthorized clients.
+                     '';
+                   };
+
+                   clientNames = mkOption {
+                     type = types.nonEmptyListOf (types.strMatching "[A-Za-z0-9+-_]+");
+                     description = ''
+                       Only clients that are listed here are authorized to access the hidden service.
+                       Generated authorization data can be found in <filename>${torDirectory}/onion/$name/hostname</filename>.
+                       Clients need to put this authorization data in their configuration file using <literal>HidServAuth</literal>.
+                     '';
+                   };
+                 };
+               }));
+             };
           };
 
           config = {
@@ -581,14 +703,10 @@ in
         after    = [ "network.target" ];
         restartTriggers = [ torRcFile ];
 
-        # Translated from the upstream contrib/dist/tor.service.in
-        preStart = ''
-          install -o tor -g tor -d ${torDirectory}/onion
-          ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config
-        '';
-
         serviceConfig =
           { Type         = "simple";
+            # Translated from the upstream contrib/dist/tor.service.in
+            ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config";
             ExecStart    = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0";
             ExecReload   = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
             KillSignal   = "SIGINT";
@@ -603,11 +721,13 @@ in
             #   DeviceAllow /dev/urandom r
             # .. but we can't specify DeviceAllow multiple times. 'closed'
             # is close enough.
+            RuntimeDirectory        = "tor";
+            StateDirectory          = [ "tor" "tor/onion" ];
             PrivateTmp              = "yes";
             DevicePolicy            = "closed";
             InaccessibleDirectories = "/home";
             ReadOnlyDirectories     = "/";
-            ReadWriteDirectories    = torDirectory;
+            ReadWriteDirectories    = [torDirectory torRunDirectory];
             NoNewPrivileges         = "yes";
           };
       };
diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix
index a29cb3f33dae..08da726437ea 100644
--- a/nixos/modules/services/security/torify.nix
+++ b/nixos/modules/services/security/torify.nix
@@ -7,7 +7,7 @@ let
   torify = pkgs.writeTextFile {
     name = "tsocks";
     text = ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         TSOCKS_CONF_FILE=${pkgs.writeText "tsocks.conf" cfg.tsocks.config} LD_PRELOAD="${pkgs.tsocks}/lib/libtsocks.so $LD_PRELOAD" "$@"
     '';
     executable = true;
diff --git a/nixos/modules/services/security/torsocks.nix b/nixos/modules/services/security/torsocks.nix
index 1b5a05b21e77..c60c745443bc 100644
--- a/nixos/modules/services/security/torsocks.nix
+++ b/nixos/modules/services/security/torsocks.nix
@@ -23,7 +23,7 @@ let
   wrapTorsocks = name: server: pkgs.writeTextFile {
     name = name;
     text = ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         TORSOCKS_CONF_FILE=${pkgs.writeText "torsocks.conf" (configFile server)} ${pkgs.torsocks}/bin/torsocks "$@"
     '';
     executable = true;
diff --git a/nixos/modules/services/security/usbguard.nix b/nixos/modules/services/security/usbguard.nix
index 1f2c56a9efa1..5d469cabe2cb 100644
--- a/nixos/modules/services/security/usbguard.nix
+++ b/nixos/modules/services/security/usbguard.nix
@@ -56,7 +56,7 @@ in {
       };
 
       rules = mkOption {
-        type = types.nullOr types.str;
+        type = types.nullOr types.lines;
         default = null;
         example = ''
           allow with-interface equals { 08:*:* }
@@ -192,7 +192,7 @@ in {
 
       serviceConfig = {
         Type = "simple";
-        ExecStart = ''${pkgs.usbguard}/bin/usbguard-daemon -d -k -c ${daemonConfFile}'';
+        ExecStart = ''${pkgs.usbguard}/bin/usbguard-daemon -P -d -k -c ${daemonConfFile}'';
         Restart = "on-failure";
       };
     };
diff --git a/nixos/modules/services/system/localtime.nix b/nixos/modules/services/system/localtime.nix
new file mode 100644
index 000000000000..b9355bbb9441
--- /dev/null
+++ b/nixos/modules/services/system/localtime.nix
@@ -0,0 +1,60 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.localtime;
+in {
+  options = {
+    services.localtime = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable <literal>localtime</literal>, simple daemon for keeping the system
+          timezone up-to-date based on the current location. It uses geoclue2 to
+          determine the current location and systemd-timedated to actually set
+          the timezone.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.geoclue2.enable = true;
+
+    security.polkit.extraConfig = ''
+     polkit.addRule(function(action, subject) {
+       if (action.id == "org.freedesktop.timedate1.set-timezone"
+           && subject.user == "localtimed") {
+         return polkit.Result.YES;
+       }
+     });
+    '';
+
+    users.users = [{
+      name = "localtimed";
+      description = "Taskserver user";
+    }];
+
+    systemd.services.localtime = {
+      description = "localtime service";
+      wantedBy = [ "multi-user.target" ];
+      partOf = [ "geoclue.service "];
+
+      serviceConfig = {
+        Restart                 = "on-failure";
+        # TODO: make it work with dbus
+        #DynamicUser             = true;
+        Nice                    = 10;
+        User                    = "localtimed";
+        PrivateTmp              = "yes";
+        PrivateDevices          = true;
+        PrivateNetwork          = "yes";
+        NoNewPrivileges         = "yes";
+        ProtectSystem           = "strict";
+        ProtectHome             = true;
+        ExecStart               = "${pkgs.localtime}/bin/localtimed";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index dd6b585b7e23..3564afd77f41 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -21,6 +21,19 @@ let
 
   # for users in group "transmission" to have access to torrents
   fullSettings = { umask = 2; download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings;
+
+  # Directories transmission expects to exist and be ug+rwx.
+  directoriesToManage = [ homeDir settingsDir fullSettings.download-dir fullSettings.incomplete-dir ];
+
+  preStart = pkgs.writeScript "transmission-pre-start" ''
+    #!${pkgs.runtimeShell}
+    set -ex
+    for DIR in ${escapeShellArgs directoriesToManage}; do
+      mkdir -p "$DIR"
+      chmod 770 "$DIR"
+    done
+    cp -f ${settingsFile} ${settingsDir}/settings.json
+  '';
 in
 {
   options = {
@@ -59,8 +72,8 @@ in
           time the service starts). String values must be quoted, integer and
           boolean values must not.
 
-          See https://trac.transmissionbt.com/wiki/EditConfigFiles for
-          documentation.
+          See https://github.com/transmission/transmission/wiki/Editing-Configuration-Files
+          for documentation.
         '';
       };
 
@@ -89,9 +102,7 @@ in
 
       # 1) Only the "transmission" user and group have access to torrents.
       # 2) Optionally update/force specific fields into the configuration file.
-      serviceConfig.ExecStartPre = ''
-          ${pkgs.stdenv.shell} -c "mkdir -p ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && chmod 770 ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && rm -f ${settingsDir}/settings.json && cp -f ${settingsFile} ${settingsDir}/settings.json"
-      '';
+      serviceConfig.ExecStartPre = preStart;
       serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port}";
       serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
       serviceConfig.User = "transmission";
@@ -136,6 +147,7 @@ in
           ${getLib pkgs.libcap}/lib/libcap*.so*            mr,
           ${getLib pkgs.attr}/lib/libattr*.so*             mr,
           ${getLib pkgs.lz4}/lib/liblz4*.so*               mr,
+          ${getLib pkgs.libkrb5}/lib/lib*.so*              mr,
 
           @{PROC}/sys/kernel/random/uuid   r,
           @{PROC}/sys/vm/overcommit_memory r,
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index 3429397d2cc2..b50de496e975 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -64,8 +64,8 @@ in
 
   config = {
     # Note: this is set here rather than up there so that changing
-    # nixosLabel would not rebuild manual pages
-    services.mingetty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixosLabel} (\m) - \l >>>'';
+    # nixos.label would not rebuild manual pages
+    services.mingetty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixos.label} (\m) - \l >>>'';
 
     systemd.services."getty@" =
       { serviceConfig.ExecStart = [
diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix
index 81ee8154326c..13c5951524d9 100644
--- a/nixos/modules/services/web-apps/atlassian/jira.nix
+++ b/nixos/modules/services/web-apps/atlassian/jira.nix
@@ -155,7 +155,7 @@ in
       requires = [ "postgresql.service" ];
       after = [ "postgresql.service" ];
 
-      path = [ cfg.jrePackage ];
+      path = [ cfg.jrePackage pkgs.bash ];
 
       environment = {
         JIRA_USER = cfg.user;
diff --git a/nixos/modules/services/web-apps/piwik-doc.xml b/nixos/modules/services/web-apps/matomo-doc.xml
index a393a182d36a..456aae6cc366 100644
--- a/nixos/modules/services/web-apps/piwik-doc.xml
+++ b/nixos/modules/services/web-apps/matomo-doc.xml
@@ -2,16 +2,16 @@
          xmlns:xlink="http://www.w3.org/1999/xlink"
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
-         xml:id="module-services-piwik">
+         xml:id="module-services-matomo">
 
-  <title>Piwik</title>
+  <title>Matomo</title>
   <para>
-    Piwik is a real-time web analytics application.
-    This module configures php-fpm as backend for piwik, optionally configuring an nginx vhost as well.
+    Matomo is a real-time web analytics application.
+    This module configures php-fpm as backend for Matomo, optionally configuring an nginx vhost as well.
   </para>
 
   <para>
-    An automatic setup is not suported by piwik, so you need to configure piwik itself in the browser-based piwik setup.
+    An automatic setup is not suported by Matomo, so you need to configure Matomo itself in the browser-based Matomo setup.
   </para>
 
 
@@ -19,7 +19,7 @@
     <title>Database Setup</title>
 
     <para>
-      You also need to configure a MariaDB or MySQL database and -user for piwik yourself,
+      You also need to configure a MariaDB or MySQL database and -user for Matomo yourself,
       and enter those credentials in your browser.
       You can use passwordless database authentication via the UNIX_SOCKET authentication plugin
       with the following SQL commands:
@@ -27,20 +27,20 @@
       <programlisting>
         # For MariaDB
         INSTALL PLUGIN unix_socket SONAME 'auth_socket';
-        CREATE DATABASE piwik;
-        CREATE USER 'piwik'@'localhost' IDENTIFIED WITH unix_socket;
-        GRANT ALL PRIVILEGES ON piwik.* TO 'piwik'@'localhost';
+        CREATE DATABASE matomo;
+        CREATE USER 'matomo'@'localhost' IDENTIFIED WITH unix_socket;
+        GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
 
         # For MySQL
         INSTALL PLUGIN auth_socket SONAME 'auth_socket.so';
-        CREATE DATABASE piwik;
-        CREATE USER 'piwik'@'localhost' IDENTIFIED WITH auth_socket;
-        GRANT ALL PRIVILEGES ON piwik.* TO 'piwik'@'localhost';
+        CREATE DATABASE matomo;
+        CREATE USER 'matomo'@'localhost' IDENTIFIED WITH auth_socket;
+        GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
       </programlisting>
 
-      Then fill in <literal>piwik</literal> as database user and database name, and leave the password field blank.
-      This authentication works by allowing only the <literal>piwik</literal> unix user to authenticate as the 
-      <literal>piwik</literal> database user (without needing a password), but no other users.
+      Then fill in <literal>matomo</literal> as database user and database name, and leave the password field blank.
+      This authentication works by allowing only the <literal>matomo</literal> unix user to authenticate as the
+      <literal>matomo</literal> database user (without needing a password), but no other users.
       For more information on passwordless login, see
       <link xlink:href="https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/" />.
     </para>
@@ -55,9 +55,9 @@
     <title>Backup</title>
     <para>
       You only need to take backups of your MySQL database and the
-      <filename>/var/lib/piwik/config/config.ini.php</filename> file.
-      Use a user in the <literal>piwik</literal> group or root to access the file.
-      For more information, see <link xlink:href="https://piwik.org/faq/how-to-install/faq_138/" />.
+      <filename>/var/lib/matomo/config/config.ini.php</filename> file.
+      Use a user in the <literal>matomo</literal> group or root to access the file.
+      For more information, see <link xlink:href="https://matomo.org/faq/how-to-install/faq_138/" />.
     </para>
   </section>
 
@@ -67,14 +67,14 @@
     <itemizedlist>
       <listitem>
         <para>
-          Piwik's file integrity check will warn you.
+          Matomo's file integrity check will warn you.
           This is due to the patches necessary for NixOS, you can safely ignore this.
         </para>
       </listitem>
 
       <listitem>
         <para>
-          Piwik will warn you that the JavaScript tracker is not writable.
+          Matomo will warn you that the JavaScript tracker is not writable.
           This is because it's located in the read-only nix store.
           You can safely ignore this, unless you need a plugin that needs JavaScript tracker access.
         </para>
@@ -88,7 +88,7 @@
 
     <para>
       You can use other web servers by forwarding calls for <filename>index.php</filename> and
-      <filename>piwik.php</filename> to the <literal>/run/phpfpm-piwik.sock</literal> fastcgi unix socket.
+      <filename>piwik.php</filename> to the <literal>/run/phpfpm-matomo.sock</literal> fastcgi unix socket.
       You can use the nginx configuration in the module code as a reference to what else should be configured.
     </para>
   </section>
diff --git a/nixos/modules/services/web-apps/piwik.nix b/nixos/modules/services/web-apps/matomo.nix
index ce86c6873dd4..ef6ac9698e21 100644
--- a/nixos/modules/services/web-apps/piwik.nix
+++ b/nixos/modules/services/web-apps/matomo.nix
@@ -1,10 +1,11 @@
 { config, lib, pkgs, services, ... }:
 with lib;
 let
-  cfg = config.services.piwik;
+  cfg = config.services.matomo;
 
-  user = "piwik";
+  user = "matomo";
   dataDir = "/var/lib/${user}";
+  deprecatedDataDir = "/var/lib/piwik";
 
   pool = user;
   # it's not possible to use /run/phpfpm/${pool}.sock because /run/phpfpm/ is root:root 0770,
@@ -13,17 +14,22 @@ let
   phpExecutionUnit = "phpfpm-${pool}";
   databaseService = "mysql.service";
 
+  fqdn =
+    let
+      join = hostName: domain: hostName + optionalString (domain != null) ".${domain}";
+     in join config.networking.hostName config.networking.domain;
+
 in {
   options = {
-    services.piwik = {
+    services.matomo = {
       # NixOS PR for database setup: https://github.com/NixOS/nixpkgs/pull/6963
-      # piwik issue for automatic piwik setup: https://github.com/piwik/piwik/issues/10257
-      # TODO: find a nice way to do this when more NixOS MySQL and / or piwik automatic setup stuff is implemented.
+      # matomo issue for automatic matomo setup: https://github.com/matomo-org/matomo/issues/10257
+      # TODO: find a nice way to do this when more NixOS MySQL and / or matomo automatic setup stuff is implemented.
       enable = mkOption {
         type = types.bool;
         default = false;
         description = ''
-          Enable piwik web analytics with php-fpm backend.
+          Enable matomo web analytics with php-fpm backend.
           Either the nginx option or the webServerUser option is mandatory.
         '';
       };
@@ -32,8 +38,9 @@ in {
         type = types.nullOr types.str;
         default = null;
         example = "lighttpd";
+        # TODO: piwik.php might get renamed to matomo.php in future releases
         description = ''
-          Name of the web server user that forwards requests to the ${phpSocket} fastcgi socket for piwik if the nginx
+          Name of the web server user that forwards requests to the ${phpSocket} fastcgi socket for matomo if the nginx
           option is not used. Either this option or the nginx option is mandatory.
           If you want to use another webserver than nginx, you need to set this to that server's user
           and pass fastcgi requests to `index.php` and `piwik.php` to this socket.
@@ -55,7 +62,7 @@ in {
           catch_workers_output = yes
         '';
         description = ''
-          Settings for phpfpm's process manager. You might need to change this depending on the load for piwik.
+          Settings for phpfpm's process manager. You might need to change this depending on the load for matomo.
         '';
       };
 
@@ -65,7 +72,7 @@ in {
             (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
             {
               # enable encryption by default,
-              # as sensitive login and piwik data should not be transmitted in clear text.
+              # as sensitive login and matomo data should not be transmitted in clear text.
               options.forceSSL.default = true;
               options.enableACME.default = true;
             }
@@ -73,15 +80,19 @@ in {
         );
         default = null;
         example = {
-          serverName = "stats.$\{config.networking.hostName\}";
+          serverAliases = [
+            "matomo.$\{config.networking.domain\}"
+            "stats.$\{config.networking.domain\}"
+          ];
           enableACME = false;
         };
         description = ''
-            With this option, you can customize an nginx virtualHost which already has sensible defaults for piwik.
+            With this option, you can customize an nginx virtualHost which already has sensible defaults for matomo.
             Either this option or the webServerUser option is mandatory.
             Set this to {} to just enable the virtualHost if you don't need any customization.
-            If enabled, then by default, the serverName is piwik.$\{config.networking.hostName\}, SSL is active,
-            and certificates are acquired via ACME.
+            If enabled, then by default, the <option>serverName</option> is
+            <literal>${user}.$\{config.networking.hostName\}.$\{config.networking.domain\}</literal>,
+            SSL is active, and certificates are acquired via ACME.
             If this is set to null (the default), no nginx virtualHost will be configured.
         '';
       };
@@ -90,12 +101,12 @@ in {
 
   config = mkIf cfg.enable {
     warnings = mkIf (cfg.nginx != null && cfg.webServerUser != null) [
-      "If services.piwik.nginx is set, services.piwik.nginx.webServerUser is ignored and should be removed."
+      "If services.matomo.nginx is set, services.matomo.nginx.webServerUser is ignored and should be removed."
     ];
 
     assertions = [ {
         assertion = cfg.nginx != null || cfg.webServerUser != null;
-        message = "Either services.piwik.nginx or services.piwik.nginx.webServerUser is mandatory";
+        message = "Either services.matomo.nginx or services.matomo.nginx.webServerUser is mandatory";
     }];
 
     users.extraUsers.${user} = {
@@ -106,19 +117,20 @@ in {
     };
     users.extraGroups.${user} = {};
 
-    systemd.services.piwik_setup_update = {
-      # everything needs to set up and up to date before piwik php files are executed
+    systemd.services.matomo_setup_update = {
+      # everything needs to set up and up to date before matomo php files are executed
       requiredBy = [ "${phpExecutionUnit}.service" ];
       before = [ "${phpExecutionUnit}.service" ];
       # the update part of the script can only work if the database is already up and running
       requires = [ databaseService ];
       after = [ databaseService ];
-      path = [ pkgs.piwik ];
+      path = [ pkgs.matomo ];
       serviceConfig = {
         Type = "oneshot";
         User = user;
         # hide especially config.ini.php from other
         UMask = "0007";
+        # TODO: might get renamed to MATOMO_USER_PATH in future versions
         Environment = "PIWIK_USER_PATH=${dataDir}";
         # chown + chmod in preStart needs root
         PermissionsStartOnly = true;
@@ -127,27 +139,32 @@ in {
       # e.g. after restoring from backup or moving from another system.
       # Note that ${dataDir}/config/config.ini.php might contain the MySQL password.
       preStart = ''
+        # migrate data from piwik to matomo folder
+        if [ -d ${deprecatedDataDir} ]; then
+          echo "Migrating from ${deprecatedDataDir} to ${dataDir}"
+          mv -T ${deprecatedDataDir} ${dataDir}
+        fi
         chown -R ${user}:${user} ${dataDir}
         chmod -R ug+rwX,o-rwx ${dataDir}
         '';
       script = ''
-            # Use User-Private Group scheme to protect piwik data, but allow administration / backup via piwik group
+            # Use User-Private Group scheme to protect matomo data, but allow administration / backup via matomo group
             # Copy config folder
             chmod g+s "${dataDir}"
-            cp -r "${pkgs.piwik}/config" "${dataDir}/"
+            cp -r "${pkgs.matomo}/config" "${dataDir}/"
             chmod -R u+rwX,g+rwX,o-rwx "${dataDir}"
 
             # check whether user setup has already been done
             if test -f "${dataDir}/config/config.ini.php"; then
               # then execute possibly pending database upgrade
-              piwik-console core:update --yes
+              matomo-console core:update --yes
             fi
       '';
     };
 
     systemd.services.${phpExecutionUnit} = {
-      # stop phpfpm on package upgrade, do database upgrade via piwik_setup_update, and then restart
-      restartTriggers = [ pkgs.piwik ];
+      # stop phpfpm on package upgrade, do database upgrade via matomo_setup_update, and then restart
+      restartTriggers = [ pkgs.matomo ];
       # stop config.ini.php from getting written with read permission for others
       serviceConfig.UMask = "0007";
     };
@@ -175,14 +192,14 @@ in {
       # References:
       # https://fralef.me/piwik-hardening-with-nginx-and-php-fpm.html
       # https://github.com/perusio/piwik-nginx
-      "${user}.${config.networking.hostName}" = mkMerge [ cfg.nginx {
-        # don't allow to override the root easily, as it will almost certainly break piwik.
+      "${user}.${fqdn}" = mkMerge [ cfg.nginx {
+        # don't allow to override the root easily, as it will almost certainly break matomo.
         # disadvantage: not shown as default in docs.
-        root = mkForce "${pkgs.piwik}/share";
+        root = mkForce "${pkgs.matomo}/share";
 
         # define locations here instead of as the submodule option's default
         # so that they can easily be extended with additional locations if required
-        # without needing to redefine the piwik ones.
+        # without needing to redefine the matomo ones.
         # disadvantage: not shown as default in docs.
         locations."/" = {
           index = "index.php";
@@ -191,6 +208,7 @@ in {
         locations."= /index.php".extraConfig = ''
           fastcgi_pass unix:${phpSocket};
         '';
+        # TODO: might get renamed to matomo.php in future versions
         # allow piwik.php for tracking
         locations."= /piwik.php".extraConfig = ''
           fastcgi_pass unix:${phpSocket};
@@ -212,6 +230,7 @@ in {
         locations."= /robots.txt".extraConfig = ''
           return 200 "User-agent: *\nDisallow: /\n";
         '';
+        # TODO: might get renamed to matomo.js in future versions
         # let browsers cache piwik.js
         locations."= /piwik.js".extraConfig = ''
           expires 1M;
@@ -221,7 +240,7 @@ in {
   };
 
   meta = {
-    doc = ./piwik-doc.xml;
+    doc = ./matomo-doc.xml;
     maintainers = with stdenv.lib.maintainers; [ florianjacob ];
   };
 }
diff --git a/nixos/modules/services/web-apps/nexus.nix b/nixos/modules/services/web-apps/nexus.nix
index a750aa66b27c..f6a5ce73a12b 100644
--- a/nixos/modules/services/web-apps/nexus.nix
+++ b/nixos/modules/services/web-apps/nexus.nix
@@ -11,7 +11,7 @@ in
 {
   options = {
     services.nexus = {
-      enable = mkEnableOption "SonarType Nexus3 OSS service";
+      enable = mkEnableOption "Sonatype Nexus3 OSS service";
 
       user = mkOption {
         type = types.str;
@@ -54,7 +54,7 @@ in
     users.extraGroups."${cfg.group}" = {};
 
     systemd.services.nexus = {
-      description = "SonarType Nexus3";
+      description = "Sonatype Nexus3";
 
       wantedBy = [ "multi-user.target" ];
 
diff --git a/nixos/modules/services/web-apps/nixbot.nix b/nixos/modules/services/web-apps/nixbot.nix
deleted file mode 100644
index 0592d01bf369..000000000000
--- a/nixos/modules/services/web-apps/nixbot.nix
+++ /dev/null
@@ -1,149 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.nixbot;
-  pyramidIni = ''
-    ###
-    # app configuration
-    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html
-    ###
-
-    [app:main]
-    use = egg:nixbot
-
-    nixbot.github_token = ${cfg.githubToken}
-    nixbot.bot_name = ${cfg.botName}
-    nixbot.repo = ${cfg.repo}
-    nixbot.pr_repo = ${cfg.prRepo}
-    nixbot.hydra_jobsets_repo = ${cfg.hydraJobsetsRepo}
-    nixbot.github_secret = justnotsorandom
-    nixbot.public_url = ${cfg.publicUrl}
-    nixbot.repo_dir = ${cfg.repoDir}
-
-    pyramid.reload_templates = false
-    pyramid.debug_authorization = false
-    pyramid.debug_notfound = false
-    pyramid.debug_routematch = false
-    pyramid.default_locale_name = en
-
-    # By default, the toolbar only appears for clients from IP addresses
-    # '127.0.0.1' and '::1'.
-    # debugtoolbar.hosts = 127.0.0.1 ::1
-
-    ###
-    # wsgi server configuration
-    ###
-
-    [server:main]
-    use = egg:waitress#main
-    host = 0.0.0.0
-    port = 6543
-
-    ###
-    # logging configuration
-    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html
-    ###
-
-    [loggers]
-    keys = root, nixbot
-
-    [handlers]
-    keys = console
-
-    [formatters]
-    keys = generic
-
-    [logger_root]
-    level = INFO
-    handlers = console
-
-    [logger_nixbot]
-    level = INFO
-    handlers =
-    qualname = nixbot
-
-    [handler_console]
-    class = StreamHandler
-    args = (sys.stderr,)
-    level = NOTSET
-    formatter = generic
-
-    [formatter_generic]
-    format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
-  '';
-in {
-  options = {
-    services.nixbot = {
-      enable = mkEnableOption "nixbot";
-
-      botName = mkOption {
-        type = types.str;
-        description = "The bot's github user account name.";
-        default = "nixbot";
-      };
-
-      githubToken = mkOption {
-        type = types.str;
-        description = "The bot's github user account token.";
-        example = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
-      };
-
-      repo = mkOption {
-        type = types.str;
-        description = "The github repository to check for PRs.";
-        example = "nixos/nixpkgs";
-      };
-
-      prRepo = mkOption {
-        type = types.str;
-        description = "The github repository to push the testing branches to.";
-        example = "nixos/nixpkgs-pr";
-      };
-
-      hydraJobsetsRepo = mkOption {
-        type = types.str;
-        description = "The github repository to push the hydra jobset definitions to.";
-        example = "nixos/hydra-jobsets";
-      };
-
-      publicUrl = mkOption {
-        type = types.str;
-        description = "The public URL the bot is reachable at (Github hook endpoint).";
-        example = "https://nixbot.nixos.org";
-      };
-
-      repoDir = mkOption {
-        type = types.path;
-        description = "The directory the repositories are stored in.";
-        default = "/var/lib/nixbot";
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    users.extraUsers.nixbot = {
-      createHome = true;
-      home = cfg.repoDir;
-    };
-
-    systemd.services.nixbot = let
-      env = pkgs.python3.buildEnv.override {
-        extraLibs = [ pkgs.nixbot ];
-      };
-    in {
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        ${env}/bin/pserve ${pkgs.writeText "production.ini" pyramidIni}
-      '';
-
-      serviceConfig = {
-        User = "nixbot";
-        Group = "nogroup";
-        PermissionsStartOnly = true;
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/web-apps/pump.io-configure.js b/nixos/modules/services/web-apps/pump.io-configure.js
deleted file mode 100644
index 1fbf346a34c4..000000000000
--- a/nixos/modules/services/web-apps/pump.io-configure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-var fs = require('fs');
-
-var opts = JSON.parse(fs.readFileSync("/dev/stdin").toString());
-var config = opts.config;
-
-var readSecret = function(filename) {
-  return fs.readFileSync(filename).toString().trim();
-};
-
-if (opts.secretFile) {
-  config.secret = readSecret(opts.secretFile);
-}
-if (opts.dbPasswordFile) {
-  config.params.dbpass = readSecret(opts.dbPasswordFile);
-}
-if (opts.smtpPasswordFile) {
-  config.smtppass = readSecret(opts.smtpPasswordFile);
-}
-if (opts.spamClientSecretFile) {
-  config.spamclientsecret = readSecret(opts.opts.spamClientSecretFile);
-}
-
-fs.writeFileSync(opts.outputFile, JSON.stringify(config));
diff --git a/nixos/modules/services/web-apps/pump.io.nix b/nixos/modules/services/web-apps/pump.io.nix
deleted file mode 100644
index 27ae68516367..000000000000
--- a/nixos/modules/services/web-apps/pump.io.nix
+++ /dev/null
@@ -1,438 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.pumpio;
-  dataDir = "/var/lib/pump.io";
-  runDir = "/run/pump.io";
-  user = "pumpio";
-
-  optionalSet = condition: value: if condition then value else {};
-
-  configScript = ./pump.io-configure.js;
-  configOptions = {
-    outputFile = "${runDir}/config.json";
-    config =
-      (optionalSet (cfg.driver != "disk") {
-        driver = cfg.driver;
-      }) //
-      {
-        params = (optionalSet (cfg.driver == "disk") { dir = dataDir; }) //
-                 (optionalSet (cfg.driver == "mongodb" || cfg.driver == "redis") {
-                   host = cfg.dbHost;
-                   port = cfg.dbPort;
-                   dbname = cfg.dbName;
-                   dbuser = cfg.dbUser;
-                   dbpass = cfg.dbPassword;
-                 }) //
-                 (optionalSet (cfg.driver == "memcached") {
-                   host = cfg.dbHost;
-                   port = cfg.dbPort;
-                 }) // cfg.driverParams;
-        secret = cfg.secret;
-
-        address = cfg.address;
-        port = cfg.port;
-
-        noweb = false;
-        urlPort = cfg.urlPort;
-        hostname = cfg.hostname;
-        favicon = cfg.favicon;
-
-        site = cfg.site;
-        owner = cfg.owner;
-        ownerURL = cfg.ownerURL;
-
-        key = cfg.sslKey;
-        cert = cfg.sslCert;
-        bounce = false;
-
-        spamhost = cfg.spamHost;
-        spamclientid = cfg.spamClientId;
-        spamclientsecret = cfg.spamClientSecret;
-
-        requireEmail = cfg.requireEmail;
-        smtpserver = cfg.smtpHost;
-        smtpport = cfg.smtpPort;
-        smtpuser = cfg.smtpUser;
-        smtppass = cfg.smtpPassword;
-        smtpusessl = cfg.smtpUseSSL;
-        smtpfrom = cfg.smtpFrom;
-
-        nologger = false;
-        enableUploads = cfg.enableUploads;
-        datadir = dataDir;
-        debugClient = false;
-        firehose = cfg.firehose;
-        disableRegistration = cfg.disableRegistration;
-
-        inherit (cfg) secretFile dbPasswordFile smtpPasswordFile spamClientSecretFile;
-      } //
-      (optionalSet (cfg.port < 1024) {
-        serverUser = user;  # have pump.io listen then drop privileges
-      }) // cfg.extraConfig;
-}; in {
-  options = {
-
-    services.pumpio = {
-
-      enable = mkEnableOption "Pump.io social streams server";
-
-      secret = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "my dog has fleas";
-        description = ''
-          A session-generating secret, server-wide password.  Warning:
-          this is stored in cleartext in the Nix store!
-        '';
-      };
-
-      secretFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-secret";
-        description = ''
-          A file containing the session-generating secret,
-          server-wide password.
-        '';
-      };
-
-      site = mkOption {
-        type = types.str;
-        example = "Awesome Sauce";
-        description = "Name of the server";
-      };
-
-      owner = mkOption {
-        type = types.str;
-        default = "";
-        example = "Awesome Inc.";
-        description = "Name of owning entity, if you want to link to it.";
-      };
-
-      ownerURL = mkOption {
-        type = types.str;
-        default = "";
-        example = "https://pump.io";
-        description = "URL of owning entity, if you want to link to it.";
-      };
-
-      address = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = ''
-          Web server listen address.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 31337;
-        description = ''
-          Port to listen on. Defaults to 31337, which is suitable for
-          running behind a reverse proxy. For a standalone server,
-          use 443.
-        '';
-      };
-
-      hostname = mkOption {
-        type = types.nullOr types.str;
-        default = "localhost";
-        description = ''
-          The hostname of the server, used for generating
-          URLs. Defaults to "localhost" which doesn't do much for you.
-        '';
-      };
-
-      urlPort = mkOption {
-        type = types.int;
-        default = 443;
-        description = ''
-          Port to use for generating URLs. This basically has to be
-          either 80 or 443 because the host-meta and Webfinger
-          protocols don't make any provision for HTTP/HTTPS servers
-          running on other ports.
-        '';
-      };
-
-      favicon = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        description = ''
-          Local filesystem path to the favicon.ico file to use. This
-          will be served as "/favicon.ico" by the server.
-        '';
-      };
-
-      enableUploads = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          If you want to disable file uploads, set this to false. Uploaded files will be stored
-          in ${dataDir}/uploads.
-        '';
-      };
-
-      sslKey = mkOption {
-        type = types.path;
-        example = "${dataDir}/myserver.key";
-        default = "";
-        description = ''
-          The path to the server certificate private key. The
-          certificate is required, but it can be self-signed.
-        '';
-      };
-
-      sslCert = mkOption {
-        type = types.path;
-        example = "${dataDir}/myserver.crt";
-        default = "";
-        description = ''
-          The path to the server certificate. The certificate is
-          required, but it can be self-signed.
-        '';
-      };
-
-      firehose = mkOption {
-        type = types.str;
-        default = "ofirehose.com";
-        description = ''
-          Firehose host running the ofirehose software. Defaults to
-          "ofirehose.com". Public notices will be ping this firehose
-          server and from there go out to search engines and the
-          world. If you want to disconnect from the public web, set
-          this to something falsy.
-        '';
-      };
-
-      disableRegistration = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Disables registering new users on the site through the Web
-          or the API.
-        '';
-      };
-
-      requireEmail = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Require an e-mail address to register.";
-      };
-
-      extraConfig = mkOption {
-        default = { };
-        description = ''
-          Extra configuration options which are serialized to json and added
-          to the pump.io.json config file.
-        '';
-      };
-
-      driver = mkOption {
-        type = types.enum [ "mongodb" "disk" "lrucache" "memcached" "redis" ];
-        default = "mongodb";
-        description = "Type of database. Corresponds to a nodejs databank driver.";
-      };
-
-      driverParams = mkOption {
-        default = { };
-        description = "Extra parameters for the driver.";
-      };
-
-      dbHost = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = "The database host to connect to.";
-      };
-
-      dbPort = mkOption {
-        type = types.int;
-        default = 27017;
-        description = "The port that the database is listening on.";
-      };
-
-      dbName = mkOption {
-        type = types.str;
-        default = "pumpio";
-        description = "The name of the database to use.";
-      };
-
-      dbUser = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          The username. Defaults to null, meaning no authentication.
-        '';
-      };
-
-      dbPassword = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          The password corresponding to dbUser.  Warning: this is
-          stored in cleartext in the Nix store!
-        '';
-      };
-
-      dbPasswordFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-dbpassword";
-        description = ''
-          A file containing the password corresponding to dbUser.
-        '';
-      };
-
-      smtpHost = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "localhost";
-        description = ''
-          Server to use for sending transactional email. If it's not
-          set up, no email is sent and features like password recovery
-          and email notification won't work.
-        '';
-      };
-
-      smtpPort = mkOption {
-        type = types.int;
-        default = 25;
-        description = ''
-          Port to connect to on SMTP server.
-        '';
-      };
-
-      smtpUser = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Username to use to connect to SMTP server. Might not be
-          necessary for some servers.
-        '';
-      };
-
-      smtpPassword = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Password to use to connect to SMTP server. Might not be
-          necessary for some servers.  Warning: this is stored in
-          cleartext in the Nix store!
-        '';
-      };
-
-      smtpPasswordFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-smtppassword";
-        description = ''
-          A file containing the password used to connect to SMTP
-          server. Might not be necessary for some servers.
-        '';
-      };
-
-
-      smtpUseSSL = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Only use SSL with the SMTP server. By default, a SSL
-          connection is negotiated using TLS. You may need to change
-          the smtpPort value if you set this.
-        '';
-      };
-
-      smtpFrom = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Email address to use in the "From:" header of outgoing
-          notifications. Defaults to 'no-reply@' plus the site
-          hostname.
-        '';
-      };
-
-      spamHost = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Host running activityspam software to use to test updates
-          for spam.
-        '';
-      };
-      spamClientId = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = "OAuth pair for spam server.";
-      };
-      spamClientSecret = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          OAuth pair for spam server.  Warning: this is
-          stored in cleartext in the Nix store!
-        '';
-      };
-      spamClientSecretFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-spamclientsecret";
-        description = ''
-          A file containing the OAuth key for the spam server.
-        '';
-      };
-    };
-
-  };
-
-  config = mkIf cfg.enable {
-    warnings = let warn = k: optional (cfg.${k} != null)
-                 "config.services.pumpio.${k} is insecure. Use ${k}File instead.";
-               in concatMap warn [ "secret" "dbPassword" "smtpPassword" "spamClientSecret" ];
-
-    assertions = [
-      { assertion = !(isNull cfg.secret && isNull cfg.secretFile);
-        message = "pump.io needs a secretFile configured";
-      }
-    ];
-
-    systemd.services."pump.io" =
-      { description = "Pump.io - stream server that does most of what people really want from a social network";
-        after = [ "network.target" ];
-        wantedBy = [ "multi-user.target" ];
-
-        preStart = ''
-          mkdir -p ${dataDir}/uploads
-          mkdir -p ${runDir}
-          chown pumpio:pumpio ${dataDir}/uploads ${runDir}
-          chmod 770 ${dataDir}/uploads ${runDir}
-
-          ${pkgs.nodejs}/bin/node ${configScript} <<EOF
-          ${builtins.toJSON configOptions}
-          EOF
-
-          chgrp pumpio ${configOptions.outputFile}
-          chmod 640 ${configOptions.outputFile}
-        '';
-
-        serviceConfig = {
-          ExecStart = "${pkgs.pumpio}/bin/pump -c ${configOptions.outputFile}";
-          PermissionsStartOnly = true;
-          User = if cfg.port < 1024 then "root" else user;
-          Group = user;
-        };
-        environment = { NODE_ENV = "production"; };
-      };
-
-      users.extraGroups.pumpio.gid = config.ids.gids.pumpio;
-      users.extraUsers.pumpio = {
-        group = "pumpio";
-        uid = config.ids.uids.pumpio;
-        description = "Pump.io user";
-        home = dataDir;
-        createHome = true;
-      };
-  };
-}
diff --git a/nixos/modules/services/web-apps/restya-board.nix b/nixos/modules/services/web-apps/restya-board.nix
new file mode 100644
index 000000000000..cee725e8fe5f
--- /dev/null
+++ b/nixos/modules/services/web-apps/restya-board.nix
@@ -0,0 +1,384 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+# TODO: are these php-packages needed?
+#imagick
+#php-geoip -> php.ini: extension = geoip.so
+#expat
+
+let
+  cfg = config.services.restya-board;
+
+  runDir = "/run/restya-board";
+
+  poolName = "restya-board";
+  phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.restya-board = {
+
+      enable = mkEnableOption "restya-board";
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/restya-board";
+        example = "/var/lib/restya-board";
+        description = ''
+          Data of the application.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "restya-board";
+        example = "restya-board";
+        description = ''
+          User account under which the web-application runs.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nginx";
+        example = "nginx";
+        description = ''
+          Group account under which the web-application runs.
+        '';
+      };
+
+      virtualHost = {
+        serverName = mkOption {
+          type = types.str;
+          default = "restya.board";
+          description = ''
+            Name of the nginx virtualhost to use.
+          '';
+        };
+
+        listenHost = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = ''
+            Listen address for the virtualhost to use.
+          '';
+        };
+
+        listenPort = mkOption {
+          type = types.int;
+          default = 3000;
+          description = ''
+            Listen port for the virtualhost to use.
+          '';
+        };
+      };
+
+      database = {
+        host = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Host of the database. Leave 'null' to use a local PostgreSQL database.
+            A local PostgreSQL database is initialized automatically.
+          '';
+        };
+
+        port = mkOption {
+          type = types.nullOr types.int;
+          default = 5432;
+          description = ''
+            The database's port.
+          '';
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "restya_board";
+          description = ''
+            Name of the database. The database must exist.
+          '';
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "restya_board";
+          description = ''
+            The database user. The user must exist and have access to
+            the specified database.
+          '';
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            The database user's password. 'null' if no password is set.
+          '';
+        };
+      };
+
+      email = {
+        server = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "localhost";
+          description = ''
+            Hostname to send outgoing mail. Null to use the system MTA.
+          '';
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 25;
+          description = ''
+            Port used to connect to SMTP server.
+          '';
+        };
+
+        login = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            SMTP authentication login used when sending outgoing mail.
+          '';
+        };
+
+        password = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            SMTP authentication password used when sending outgoing mail.
+
+            ATTENTION: The password is stored world-readable in the nix-store!
+          '';
+        };
+      };
+
+      timezone = mkOption {
+        type = types.lines;
+        default = "GMT";
+        description = ''
+          Timezone the web-app runs in.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.phpfpm.poolConfigs = {
+      "${poolName}" = ''
+        listen = "${phpfpmSocketName}";
+        listen.owner = nginx
+        listen.group = nginx
+        listen.mode = 0600
+        user = ${cfg.user}
+        group = ${cfg.group}
+        pm = dynamic
+        pm.max_children = 75
+        pm.start_servers = 10
+        pm.min_spare_servers = 5
+        pm.max_spare_servers = 20
+        pm.max_requests = 500
+        catch_workers_output = 1
+      '';
+    };
+
+    services.phpfpm.phpOptions = ''
+      date.timezone = "CET"
+
+      ${optionalString (!isNull cfg.email.server) ''
+        SMTP = ${cfg.email.server}
+        smtp_port = ${toString cfg.email.port}
+        auth_username = ${cfg.email.login}
+        auth_password = ${cfg.email.password}
+      ''}
+    '';
+
+    services.nginx.enable = true;
+    services.nginx.virtualHosts."${cfg.virtualHost.serverName}" = {
+      listen = [ { addr = cfg.virtualHost.listenHost; port = cfg.virtualHost.listenPort; } ];
+      serverName = cfg.virtualHost.serverName;
+      root = runDir;
+      extraConfig = ''
+        index index.html index.php;
+
+        gzip on;
+        gzip_disable "msie6";
+
+        gzip_comp_level 6;
+        gzip_min_length  1100;
+        gzip_buffers 16 8k;
+        gzip_proxied any;
+        gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss;
+
+        client_max_body_size 300M;
+
+        rewrite ^/oauth/authorize$ /server/php/authorize.php last;
+        rewrite ^/oauth_callback/([a-zA-Z0-9_\.]*)/([a-zA-Z0-9_\.]*)$ /server/php/oauth_callback.php?plugin=$1&code=$2 last;
+        rewrite ^/download/([0-9]*)/([a-zA-Z0-9_\.]*)$ /server/php/download.php?id=$1&hash=$2 last;
+        rewrite ^/ical/([0-9]*)/([0-9]*)/([a-z0-9]*).ics$ /server/php/ical.php?board_id=$1&user_id=$2&hash=$3 last;
+        rewrite ^/api/(.*)$ /server/php/R/r.php?_url=$1&$args last;
+        rewrite ^/api_explorer/api-docs/$ /client/api_explorer/api-docs/index.php last;
+      '';
+
+      locations."/".root = "${runDir}/client";
+
+      locations."~ \.php$" = {
+        tryFiles = "$uri =404";
+        extraConfig = ''
+          include ${pkgs.nginx}/conf/fastcgi_params;
+          fastcgi_pass    unix:${phpfpmSocketName};
+          fastcgi_index   index.php;
+          fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
+          fastcgi_param   PHP_VALUE "upload_max_filesize=9G \n post_max_size=9G \n max_execution_time=200 \n max_input_time=200 \n memory_limit=256M";
+        '';
+      };
+
+      locations."~* \.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
+        root = "${runDir}/client";
+        extraConfig = ''
+          if (-f $request_filename) {
+                  break;
+          }
+          rewrite ^/img/([a-zA-Z_]*)/([a-zA-Z_]*)/([a-zA-Z0-9_\.]*)$ /server/php/image.php?size=$1&model=$2&filename=$3 last;
+          add_header        Cache-Control public;
+          add_header        Cache-Control must-revalidate;
+          expires           7d;
+        '';
+      };
+    };
+
+    systemd.services.restya-board-init = {
+      description = "Restya board initialization";
+      serviceConfig.Type = "oneshot";
+      serviceConfig.RemainAfterExit = true;
+
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "postgresql.service" ];
+      after = [ "network.target" "postgresql.service" ];
+
+      script = ''
+        rm -rf "${runDir}"
+        mkdir -m 750 -p "${runDir}"
+        cp -r "${pkgs.restya-board}/"* "${runDir}"
+        sed -i "s/@restya.com/@${cfg.virtualHost.serverName}/g" "${runDir}/sql/restyaboard_with_empty_data.sql"
+        rm -rf "${runDir}/media"
+        rm -rf "${runDir}/client/img"
+        chmod -R 0750 "${runDir}"
+
+        sed -i "s@^php@${config.services.phpfpm.phpPackage}/bin/php@" "${runDir}/server/php/shell/"*.sh
+
+        ${if (isNull cfg.database.host) then ''
+          sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', 'localhost');/g" "${runDir}/server/php/config.inc.php"
+          sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php"
+        '' else ''
+          sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php"
+          sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', '$(<${cfg.database.dbPassFile})');/g" "${runDir}/server/php/config.inc.php"
+        ''}
+        sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php"
+        sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php"
+        sed -i "s/^.*'R_DB_USER'.*$/define('R_DB_USER', '${cfg.database.user}');/g" "${runDir}/server/php/config.inc.php"
+
+        chmod 0400 "${runDir}/server/php/config.inc.php"
+
+        ln -sf "${cfg.dataDir}/media" "${runDir}/media"
+        ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img"
+
+        chmod g+w "${runDir}/tmp/cache"
+        chown -R "${cfg.user}"."${cfg.group}" "${runDir}"
+
+
+        mkdir -m 0750 -p "${cfg.dataDir}"
+        mkdir -m 0750 -p "${cfg.dataDir}/media"
+        mkdir -m 0750 -p "${cfg.dataDir}/client/img"
+        cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media"
+        cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img"
+        chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}"
+        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media"
+        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img"
+
+        ${optionalString (isNull cfg.database.host) ''
+          if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then
+            ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+              ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+              -c "CREATE USER ${cfg.database.user} WITH ENCRYPTED PASSWORD 'restya'"
+
+            ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+              ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+              -c "CREATE DATABASE ${cfg.database.name} OWNER ${cfg.database.user} ENCODING 'UTF8' TEMPLATE template0"
+
+            ${pkgs.sudo}/bin/sudo -u ${cfg.user} \
+              ${config.services.postgresql.package}/bin/psql -U ${cfg.database.user} \
+              -d ${cfg.database.name} -f "${runDir}/sql/restyaboard_with_empty_data.sql"
+
+            touch "${cfg.dataDir}/.db-initialized"
+          fi
+        ''}
+      '';
+    };
+
+    systemd.timers.restya-board = {
+      description = "restya-board scripts for e.g. email notification";
+      wantedBy = [ "timers.target" ];
+      after = [ "restya-board-init.service" ];
+      requires = [ "restya-board-init.service" ];
+      timerConfig = {
+        OnUnitInactiveSec = "60s";
+        Unit = "restya-board-timers.service";
+      };
+    };
+
+    systemd.services.restya-board-timers = {
+      description = "restya-board scripts for e.g. email notification";
+      serviceConfig.Type = "oneshot";
+      serviceConfig.User = cfg.user;
+
+      after = [ "restya-board-init.service" ];
+      requires = [ "restya-board-init.service" ];
+
+      script = ''
+        /bin/sh ${runDir}/server/php/shell/instant_email_notification.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/periodic_email_notification.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/imap.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/webhook.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/card_due_notification.sh 2> /dev/null || true
+      '';
+    };
+
+    users.extraUsers.restya-board = {
+      isSystemUser = true;
+      createHome = false;
+      home = runDir;
+      group  = "restya-board";
+    };
+    users.extraGroups.restya-board = {};
+
+    services.postgresql.enable = mkIf (isNull cfg.database.host) true;
+
+    services.postgresql.identMap = optionalString (isNull cfg.database.host)
+      ''
+        restya-board-users restya-board restya_board
+      '';
+
+    services.postgresql.authentication = optionalString (isNull cfg.database.host)
+      ''
+        local restya_board all ident map=restya-board-users
+      '';
+
+  };
+
+}
+
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 76b0ee6da968..610c6463a5eb 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -99,8 +99,8 @@ let
 
       user = mkOption {
         type = types.str;
-        default = "nginx";
-        example = "nginx";
+        default = "tt_rss";
+        example = "tt_rss";
         description = ''
           User account under which both the update daemon and the web-application run.
         '';
@@ -466,26 +466,28 @@ let
       '';
     };
 
-    services.nginx.virtualHosts = mkIf (cfg.virtualHost != null) {
-      "${cfg.virtualHost}" = {
-        root = "${cfg.root}";
-
-        locations."/" = {
-          index = "index.php";
-        };
-
-        locations."~ \.php$" = {
-          extraConfig = ''
-            fastcgi_split_path_info ^(.+\.php)(/.+)$;
-            fastcgi_pass unix:${phpfpmSocketName};
-            fastcgi_index index.php;
-            fastcgi_param SCRIPT_FILENAME ${cfg.root}/$fastcgi_script_name;
-          '';
+    # NOTE: No configuration is done if not using virtual host
+    services.nginx = mkIf (cfg.virtualHost != null) {
+      enable = true;
+      virtualHosts = {
+        "${cfg.virtualHost}" = {
+          root = "${cfg.root}";
+
+          locations."/" = {
+            index = "index.php";
+          };
+
+          locations."~ \.php$" = {
+            extraConfig = ''
+              fastcgi_split_path_info ^(.+\.php)(/.+)$;
+              fastcgi_pass unix:${phpfpmSocketName};
+              fastcgi_index index.php;
+            '';
+          };
         };
       };
     };
 
-
     systemd.services.tt-rss = let
       dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service";
     in {
@@ -496,14 +498,14 @@ let
           callSql = e:
               if cfg.database.type == "pgsql" then ''
                   ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \
-                  ${pkgs.postgresql95}/bin/psql \
+                  ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.postgresql.package}/bin/psql \
                     -U ${cfg.database.user} \
                     ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \
                     -c '${e}' \
                     ${cfg.database.name}''
 
               else if cfg.database.type == "mysql" then ''
-                  echo '${e}' | ${pkgs.mysql}/bin/mysql \
+                  echo '${e}' | ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.mysql.package}/bin/mysql \
                     -u ${cfg.database.user} \
                     ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \
                     ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \
@@ -521,6 +523,14 @@ let
         ''
 
         + (optionalString (cfg.database.type == "pgsql") ''
+          ${optionalString (cfg.database.host == null && cfg.database.password == null) ''
+            if ! [ -e ${cfg.root}/.db-created ]; then
+              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser ${cfg.database.user}
+              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O ${cfg.database.user} ${cfg.database.name}
+              touch ${cfg.root}/.db-created
+            fi
+          ''}
+
           exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
           | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
 
@@ -554,5 +564,28 @@ let
         requires = ["${dbService}"];
         after = ["network.target" "${dbService}"];
     };
+
+    services.mysql = optionalAttrs (cfg.database.type == "mysql") {
+      enable = true;
+      package = mkDefault pkgs.mysql;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        {
+          name = cfg.user;
+          ensurePermissions = {
+            "${cfg.database.name}.*" = "ALL PRIVILEGES";
+          };
+        }
+      ];
+    };
+
+    services.postgresql = optionalAttrs (cfg.database.type == "pgsql") {
+      enable = mkDefault true;
+    };
+
+    users = optionalAttrs (cfg.user == "tt_rss") {
+      extraUsers.tt_rss.group = "tt_rss";
+      extraGroups.tt_rss = {};
+    };
   };
 }
diff --git a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
index 94e85f1f4289..82b8bf3e30db 100644
--- a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
@@ -188,8 +188,7 @@ let
       /* date format to be used while writing to the owncloud logfile */
       'logdateformat' => 'F d, Y H:i:s',
 
-      /* timezone used while writing to the owncloud logfile (default: UTC) */
-      'logtimezone' => '${serverInfo.fullConfig.time.timeZone}',
+      ${tzSetting}
 
       /* Append all database queries and parameters to the log file.
        (watch out, this option can increase the size of your log file)*/
@@ -339,6 +338,31 @@ let
 
     '';
 
+  tzSetting = let tz = serverInfo.fullConfig.time.timeZone; in optionalString (!isNull tz) ''
+    /* timezone used while writing to the owncloud logfile (default: UTC) */
+    'logtimezone' => '${tz}',
+  '';
+
+  postgresql = serverInfo.fullConfig.services.postgresql.package;
+
+  setupDb = pkgs.writeScript "setup-owncloud-db" ''
+    #!${pkgs.runtimeShell}
+    PATH="${postgresql}/bin"
+    createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
+    createdb "${config.dbName}" -O "${config.dbUser}" || true
+    psql -U postgres -d postgres -c "alter user ${config.dbUser} with password '${config.dbPassword}';" || true
+
+    QUERY="CREATE TABLE appconfig
+             ( appid       VARCHAR( 255 ) NOT NULL
+             , configkey   VARCHAR( 255 ) NOT NULL
+             , configvalue VARCHAR( 255 ) NOT NULL
+             );
+           GRANT ALL ON appconfig TO ${config.dbUser};
+           ALTER TABLE appconfig OWNER TO ${config.dbUser};"
+
+    psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+  '';
+
 in
 
 rec {
@@ -353,7 +377,7 @@ rec {
       ''}
 
       <Directory ${config.package}>
-        ${builtins.readFile "${config.package}/.htaccess"}
+        Include ${config.package}/.htaccess
       </Directory>
     '';
 
@@ -373,7 +397,7 @@ rec {
       defaultText = "pkgs.owncloud70";
       example = literalExample "pkgs.owncloud70";
       description = ''
-          PostgreSQL package to use.
+          ownCloud package to use.
       '';
     };
 
@@ -574,13 +598,7 @@ rec {
       chmod -R o-rwx ${config.dataDir}
       chown -R wwwrun:wwwrun ${config.dataDir}
 
-      ${pkgs.postgresql}/bin/createuser -s -r postgres
-      ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
-      ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}" || true
-      ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -U postgres -d postgres -c "alter user ${config.dbUser} with password '${config.dbPassword}';" || true
-
-      QUERY="CREATE TABLE appconfig (appid VARCHAR( 255 ) NOT NULL ,configkey VARCHAR( 255 ) NOT NULL ,configvalue VARCHAR( 255 ) NOT NULL); GRANT ALL ON appconfig TO ${config.dbUser}; ALTER TABLE appconfig OWNER TO ${config.dbUser};"
-      ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+      ${pkgs.sudo}/bin/sudo -u postgres ${setupDb}
     fi
 
     if [ -e ${config.package}/config/ca-bundle.crt ]; then
@@ -591,7 +609,11 @@ rec {
 
     chown wwwrun:wwwrun ${config.dataDir}/owncloud.log || true
 
-    QUERY="INSERT INTO groups (gid) values('admin'); INSERT INTO users (uid,password) values('${config.adminUser}','${builtins.hashString "sha1" config.adminPassword}'); INSERT INTO group_user (gid,uid) values('admin','${config.adminUser}');"
-    ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+    QUERY="INSERT INTO groups (gid) values('admin');
+           INSERT INTO users (uid,password)
+             values('${config.adminUser}','${builtins.hashString "sha1" config.adminPassword}');
+           INSERT INTO group_user (gid,uid)
+             values('admin','${config.adminUser}');"
+    ${pkgs.sudo}/bin/sudo -u postgres ${postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
   '';
 }
diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
index 1d53ce659005..4bbd041b6e04 100644
--- a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -118,7 +118,7 @@ with lib;
     default = [];
     example = [
       { urlPath = "/foo/bar.png";
-        files = "/home/eelco/some-file.png";
+        file = "/home/eelco/some-file.png";
       }
     ];
     description = ''
diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix
index 700b4469c565..d23e810dcc62 100644
--- a/nixos/modules/services/web-servers/lighttpd/default.nix
+++ b/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -50,11 +50,14 @@ let
     "mod_geoip"
     "mod_magnet"
     "mod_mysql_vhost"
+    "mod_openssl"  # since v1.4.46
     "mod_scgi"
     "mod_setenv"
     "mod_trigger_b4_dl"
     "mod_uploadprogress"
+    "mod_vhostdb"  # since v1.4.46
     "mod_webdav"
+    "mod_wstunnel"  # since v1.4.46
   ];
 
   maybeModuleString = moduleName:
diff --git a/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
index c8d9836b0b68..c494d6966a7f 100644
--- a/nixos/modules/services/web-servers/lighttpd/gitweb.nix
+++ b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
@@ -3,12 +3,10 @@
 with lib;
 
 let
-  cfg = config.services.lighttpd.gitweb;
-  gitwebConfigFile = pkgs.writeText "gitweb.conf" ''
-    # path to git projects (<project>.git)
-    $projectroot = "${cfg.projectroot}";
-    ${cfg.extraConfig}
-  '';
+  cfg = config.services.gitweb;
+  package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme {
+    gitwebTheme = true;
+  });
 
 in
 {
@@ -23,26 +21,9 @@ in
       '';
     };
 
-    projectroot = mkOption {
-      default = "/srv/git";
-      type = types.path;
-      description = ''
-        Path to git projects (bare repositories) that should be served by
-        gitweb. Must not end with a slash.
-      '';
-    };
-
-    extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      description = ''
-        Verbatim configuration text appended to the generated gitweb.conf file.
-      '';
-    };
-
   };
 
-  config = mkIf cfg.enable {
+  config = mkIf config.services.lighttpd.gitweb.enable {
 
     # declare module dependencies
     services.lighttpd.enableModules = [ "mod_cgi" "mod_redirect" "mod_alias" "mod_setenv" ];
@@ -56,11 +37,11 @@ in
               "^/gitweb$" => "/gitweb/"
           )
           alias.url = (
-              "/gitweb/static/" => "${pkgs.git}/share/gitweb/static/",
-              "/gitweb/"        => "${pkgs.git}/share/gitweb/gitweb.cgi"
+              "/gitweb/static/" => "${package}/static/",
+              "/gitweb/"        => "${package}/gitweb.cgi"
           )
           setenv.add-environment = (
-              "GITWEB_CONFIG" => "${gitwebConfigFile}",
+              "GITWEB_CONFIG" => "${cfg.gitwebConfigFile}",
               "HOME" => "${cfg.projectroot}"
           )
       }
diff --git a/nixos/modules/services/web-servers/lighttpd/inginious.nix b/nixos/modules/services/web-servers/lighttpd/inginious.nix
index 669e81d0f14b..8c813d116a52 100644
--- a/nixos/modules/services/web-servers/lighttpd/inginious.nix
+++ b/nixos/modules/services/web-servers/lighttpd/inginious.nix
@@ -113,7 +113,6 @@ in
 
     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.
@@ -218,6 +217,7 @@ in
 
       # Common
       {
+        services.lighttpd.inginious.tasksDirectory = mkDefault "${inginious}/lib/python2.7/site-packages/inginious/tasks";
         # To access inginous tools (like inginious-test-task)
         environment.systemPackages = [ inginious ];
 
diff --git a/nixos/modules/services/web-servers/mighttpd2.nix b/nixos/modules/services/web-servers/mighttpd2.nix
new file mode 100644
index 000000000000..a888f623616e
--- /dev/null
+++ b/nixos/modules/services/web-servers/mighttpd2.nix
@@ -0,0 +1,132 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.mighttpd2;
+  configFile = pkgs.writeText "mighty-config" cfg.config;
+  routingFile = pkgs.writeText "mighty-routing" cfg.routing;
+in {
+  options.services.mighttpd2 = {
+    enable = mkEnableOption "Mighttpd2 web server";
+
+    config = mkOption {
+      default = "";
+      example = ''
+        # Example configuration for Mighttpd 2
+        Port: 80
+        # IP address or "*"
+        Host: *
+        Debug_Mode: Yes # Yes or No
+        # If available, "nobody" is much more secure for User:.
+        User: root
+        # If available, "nobody" is much more secure for Group:.
+        Group: root
+        Pid_File: /var/run/mighty.pid
+        Logging: Yes # Yes or No
+        Log_File: /var/log/mighty # The directory must be writable by User:
+        Log_File_Size: 16777216 # bytes
+        Log_Backup_Number: 10
+        Index_File: index.html
+        Index_Cgi: index.cgi
+        Status_File_Dir: /usr/local/share/mighty/status
+        Connection_Timeout: 30 # seconds
+        Fd_Cache_Duration: 10 # seconds
+        # Server_Name: Mighttpd/3.x.y
+        Tls_Port: 443
+        Tls_Cert_File: cert.pem # should change this with an absolute path
+        # should change this with comma-separated absolute paths
+        Tls_Chain_Files: chain.pem
+        # Currently, Tls_Key_File must not be encrypted.
+        Tls_Key_File: privkey.pem # should change this with an absolute path
+        Service: 0 # 0 is HTTP only, 1 is HTTPS only, 2 is both
+      '';
+      type = types.lines;
+      description = ''
+        Verbatim config file to use
+        (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+      '';
+    };
+
+    routing = mkOption {
+      default = "";
+      example = ''
+        # Example routing for Mighttpd 2
+
+        # Domain lists
+        [localhost www.example.com]
+
+        # Entries are looked up in the specified order
+        # All paths must end with "/"
+
+        # A path to CGI scripts should be specified with "=>"
+        /~alice/cgi-bin/ => /home/alice/public_html/cgi-bin/
+
+        # A path to static files should be specified with "->"
+        /~alice/         -> /home/alice/public_html/
+        /cgi-bin/        => /export/cgi-bin/
+
+        # Reverse proxy rules should be specified with ">>"
+        # /path >> host:port/path2
+        # Either "host" or ":port" can be committed, but not both.
+        /app/cal/        >> example.net/calendar/
+        # Yesod app in the same server
+        /app/wiki/       >> 127.0.0.1:3000/
+
+        /                -> /export/www/
+      '';
+      type = types.lines;
+      description = ''
+        Verbatim routing file to use
+        (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+      '';
+    };
+
+    cores = mkOption {
+      default = null;
+      type = types.nullOr types.int;
+      description = ''
+        How many cores to use.
+        If null it will be determined automatically
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.routing != "";
+          message = "You need at least one rule in mighttpd2.routing";
+        }
+      ];
+    systemd.services.mighttpd2 = {
+      description = "Mighttpd2 web server";
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.haskellPackages.mighttpd2}/bin/mighty \
+            ${configFile} \
+            ${routingFile} \
+            +RTS -N${optionalString (cfg.cores != null) "${cfg.cores}"}
+        '';
+        Type = "simple";
+        User = "mighttpd2";
+        Group = "mighttpd2";
+        Restart = "on-failure";
+        AmbientCapabilities = "cap_net_bind_service";
+        CapabilityBoundingSet = "cap_net_bind_service";
+      };
+    };
+
+    users.extraUsers.mighttpd2 = {
+      group = "mighttpd2";
+      uid = config.ids.uids.mighttpd2;
+      isSystemUser = true;
+    };
+
+    users.extraGroups.mighttpd2.gid = config.ids.gids.mighttpd2;
+  };
+
+  meta.maintainers = with lib.maintainers; [ fgaz ];
+}
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 97511aac9737..938a8a1fe334 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -9,12 +9,16 @@ let
       serverName = if vhostConfig.serverName != null
         then vhostConfig.serverName
         else vhostName;
+      acmeDirectory = config.security.acme.directory;
     in
     vhostConfig // {
       inherit serverName;
     } // (optionalAttrs vhostConfig.enableACME {
-      sslCertificate = "/var/lib/acme/${serverName}/fullchain.pem";
-      sslCertificateKey = "/var/lib/acme/${serverName}/key.pem";
+      sslCertificate = "${acmeDirectory}/${serverName}/fullchain.pem";
+      sslCertificateKey = "${acmeDirectory}/${serverName}/key.pem";
+    }) // (optionalAttrs (vhostConfig.useACMEHost != null) {
+      sslCertificate = "${acmeDirectory}/${vhostConfig.useACMEHost}/fullchain.pem";
+      sslCertificateKey = "${acmeDirectory}/${vhostConfig.useACMEHost}/key.pem";
     })
   ) cfg.virtualHosts;
   enableIPv6 = config.networking.enableIPv6;
@@ -167,13 +171,14 @@ let
 
         listenString = { addr, port, ssl, ... }:
           "listen ${addr}:${toString port} "
-          + optionalString ssl "ssl http2 "
+          + optionalString ssl "ssl "
+          + optionalString (ssl && vhost.http2) "http2 "
           + optionalString vhost.default "default_server "
           + ";";
 
         redirectListen = filter (x: !x.ssl) defaultListen;
 
-        acmeLocation = ''
+        acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) ''
           location /.well-known/acme-challenge {
             ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"}
             root ${vhost.acmeRoot};
@@ -193,7 +198,7 @@ let
             ${concatMapStringsSep "\n" listenString redirectListen}
 
             server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
-            ${optionalString vhost.enableACME acmeLocation}
+            ${acmeLocation}
             location / {
               return 301 https://$host$request_uri;
             }
@@ -203,7 +208,7 @@ let
         server {
           ${concatMapStringsSep "\n" listenString hostListen}
           server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
-          ${optionalString vhost.enableACME acmeLocation}
+          ${acmeLocation}
           ${optionalString (vhost.root != null) "root ${vhost.root};"}
           ${optionalString (vhost.globalRedirect != null) ''
             return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
@@ -554,6 +559,14 @@ in
           are mutually exclusive.
         '';
       }
+
+      {
+        assertion = all (conf: !(conf.enableACME && conf.useACMEHost != null)) (attrValues virtualHosts);
+        message = ''
+          Options services.nginx.service.virtualHosts.<name>.enableACME and
+          services.nginx.virtualHosts.<name>.useACMEHost are mutually exclusive.
+        '';
+      }
     ];
 
     systemd.services.nginx = {
@@ -566,6 +579,7 @@ in
         mkdir -p ${cfg.stateDir}/logs
         chmod 700 ${cfg.stateDir}
         chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
+        ${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir} -t
         '';
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir}";
@@ -579,7 +593,7 @@ in
     security.acme.certs = filterAttrs (n: v: v != {}) (
       let
         vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts;
-        acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME) vhostsConfigs;
+        acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME && vhostConfig.useACMEHost == null) vhostsConfigs;
         acmePairs = map (vhostConfig: { name = vhostConfig.serverName; value = {
             user = cfg.user;
             group = lib.mkDefault cfg.group;
diff --git a/nixos/modules/services/web-servers/nginx/gitweb.nix b/nixos/modules/services/web-servers/nginx/gitweb.nix
new file mode 100644
index 000000000000..272fd1480185
--- /dev/null
+++ b/nixos/modules/services/web-servers/nginx/gitweb.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gitweb;
+  package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme {
+    gitwebTheme = true;
+  });
+
+in
+{
+
+  options.services.nginx.gitweb = {
+
+    enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        If true, enable gitweb in nginx. Access it at http://yourserver/gitweb
+      '';
+    };
+
+  };
+
+  config = mkIf config.services.nginx.gitweb.enable {
+
+    systemd.services.gitweb = {
+      description = "GitWeb service";
+      script = "${package}/gitweb.cgi --fastcgi --nproc=1";
+      environment  = {
+        FCGI_SOCKET_PATH = "/run/gitweb/gitweb.sock";
+      };
+      serviceConfig = {
+        User = "nginx";
+        Group = "nginx";
+        RuntimeDirectory = [ "gitweb" ];
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    services.nginx = {
+      virtualHosts.default = {
+        locations."/gitweb/static/" = {
+          alias = "${package}/static/";
+        };
+        locations."/gitweb/" = {
+          extraConfig = ''
+            include ${pkgs.nginx}/conf/fastcgi_params;
+            fastcgi_param GITWEB_CONFIG ${cfg.gitwebConfigFile};
+            fastcgi_pass unix:/run/gitweb/gitweb.sock;
+          '';
+        };
+      };
+    };
+
+  };
+
+  meta.maintainers = with maintainers; [ gnidorah ];
+
+}
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index 801601aafd9d..bf18108a1a3c 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -48,7 +48,21 @@ with lib;
     enableACME = mkOption {
       type = types.bool;
       default = false;
-      description = "Whether to ask Let's Encrypt to sign a certificate for this vhost.";
+      description = ''
+        Whether to ask Let's Encrypt to sign a certificate for this vhost.
+        Alternately, you can use an existing certificate through <option>useACMEHost</option>.
+      '';
+    };
+
+    useACMEHost = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        A host of an existing Let's Encrypt certificate to use.
+        This is useful if you have many subdomains and want to avoid hitting the
+        <link xlink:href="https://letsencrypt.org/docs/rate-limits/">rate limit</link>.
+        Alternately, you can generate a certificate through <option>enableACME</option>.
+      '';
     };
 
     acmeRoot = mkOption {
@@ -114,6 +128,20 @@ with lib;
       description = "Path to server SSL certificate key.";
     };
 
+    http2 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to enable HTTP 2.
+        Note that (as of writing) due to nginx's implementation, to disable
+        HTTP 2 you have to disable it on all vhosts that use a given
+        IP address / port.
+        If there is one server block configured to enable http2,then it is
+        enabled for all server blocks on this IP.
+        See https://stackoverflow.com/a/39466948/263061.
+      '';
+    };
+
     root = mkOption {
       type = types.nullOr types.path;
       default = null;
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index 943415e08c64..aa94e0e976c9 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -19,27 +19,43 @@ in
   options = {
 
     services.tomcat = {
-
-      enable = mkOption {
-        default = false;
-        description = "Whether to enable Apache Tomcat";
-      };
+      enable = mkEnableOption "Apache Tomcat";
 
       package = mkOption {
         type = types.package;
         default = pkgs.tomcat85;
         defaultText = "pkgs.tomcat85";
-        example = lib.literalExample "pkgs.tomcatUnstable";
+        example = lib.literalExample "pkgs.tomcat9";
         description = ''
           Which tomcat package to use.
         '';
       };
 
       baseDir = mkOption {
+        type = lib.types.path;
         default = "/var/tomcat";
         description = "Location where Tomcat stores configuration files, webapplications and logfiles";
       };
 
+      logDirs = mkOption {
+        default = [];
+        type = types.listOf types.path;
+        description = "Directories to create in baseDir/logs/";
+      };
+
+      extraConfigFiles = mkOption {
+        default = [];
+        type = types.listOf types.path;
+        description = "Extra configuration files to pull into the tomcat conf directory";
+      };
+
+      extraEnvironment = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "ENVIRONMENT=production" ];
+        description = "Environment Variables to pass to the tomcat service";
+      };
+
       extraGroups = mkOption {
         default = [];
         example = [ "users" ];
@@ -47,31 +63,46 @@ in
       };
 
       user = mkOption {
+        type = types.str;
         default = "tomcat";
         description = "User account under which Apache Tomcat runs.";
       };
 
       group = mkOption {
+        type = types.str;
         default = "tomcat";
         description = "Group account under which Apache Tomcat runs.";
       };
 
       javaOpts = mkOption {
+        type = types.either (types.listOf types.str) types.str;
         default = "";
         description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
       };
 
       catalinaOpts = mkOption {
+        type = types.either (types.listOf types.str) types.str;
         default = "";
         description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
       };
 
       sharedLibs = mkOption {
+        type = types.listOf types.str;
         default = [];
         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
       };
 
+      serverXml = mkOption {
+        type = types.lines;
+        default = "";
+        description = "
+          Verbatim server.xml configuration.
+          This is mutually exclusive with the virtualHosts options.
+        ";
+      };
+
       commonLibs = mkOption {
+        type = types.listOf types.str;
         default = [];
         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
       };
@@ -84,11 +115,21 @@ in
       };
 
       virtualHosts = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            name = mkOption {
+              type = types.listOf types.str;
+              description = "name of the virtualhost";
+              default = [];
+            };
+          };
+        });
         default = [];
         description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
       };
 
       logPerVirtualHost = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to enable logging per virtual host.";
       };
@@ -104,11 +145,13 @@ in
 
         enable = mkOption {
           default = false;
+          type = types.bool;
           description = "Whether to enable an Apache Axis2 container";
         };
 
         services = mkOption {
           default = [];
+          type = types.listOf types.str;
           description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
         };
 
@@ -140,130 +183,104 @@ in
       description = "Apache Tomcat server";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      serviceConfig.Type = "oneshot";
-      serviceConfig.RemainAfterExit = true;
 
       preStart = ''
         # Create the base directory
-        mkdir -p ${cfg.baseDir}
+        mkdir -p \
+          ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
+        chown ${cfg.user}:${cfg.group} \
+          ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
 
         # Create a symlink to the bin directory of the tomcat component
         ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
 
-        # Create a conf/ directory
-        mkdir -p ${cfg.baseDir}/conf
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/conf
-
         # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
-        for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml)
-        do
-            ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
+        for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml); do
+          ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
         done
 
-        # Create subdirectory for virtual hosts
-        mkdir -p ${cfg.baseDir}/virtualhosts
+        ${if cfg.extraConfigFiles != [] then ''
+          for i in ${toString cfg.extraConfigFiles}; do
+            ln -sfn $i ${cfg.baseDir}/conf/`basename $i`
+          done
+        '' else ""}
 
         # Create a modified catalina.properties file
         # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
         sed -e 's|''${catalina.home}|''${catalina.base}|g' \
-            -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
-            ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
-
-        # Create a modified server.xml which also includes all virtual hosts
-        sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\  ${
-                     toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\"  prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
-            ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
-
-        # Create a logs/ directory
-        mkdir -p ${cfg.baseDir}/logs
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs
-        ${if cfg.logPerVirtualHost then
-           toString (map (h: ''
-                                mkdir -p ${cfg.baseDir}/logs/${h.name}
-                                chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
-                             '') cfg.virtualHosts) else ''''}
-
-        # Create a temp/ directory
-        mkdir -p ${cfg.baseDir}/temp
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/temp
-
-        # Create a lib/ directory
-        mkdir -p ${cfg.baseDir}/lib
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/lib
-
-        # Create a shared/lib directory
-        mkdir -p ${cfg.baseDir}/shared/lib
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/shared/lib
-
-        # Create a webapps/ directory
-        mkdir -p ${cfg.baseDir}/webapps
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps
+          -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
+          ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
+
+        ${if cfg.serverXml != "" then ''
+          cp -f ${pkgs.writeTextDir "server.xml" cfg.serverXml}/* ${cfg.baseDir}/conf/
+          '' else ''
+          # Create a modified server.xml which also includes all virtual hosts
+          sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\  ${toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\"  prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
+                ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
+          ''
+        }
+        ${optionalString (cfg.logDirs != []) ''
+          for i in ${toString cfg.logDirs}; do
+            mkdir -p ${cfg.baseDir}/logs/$i
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/$i
+          done
+        ''}
+        ${optionalString cfg.logPerVirtualHost (toString (map (h: ''
+          mkdir -p ${cfg.baseDir}/logs/${h.name}
+          chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
+        '') cfg.virtualHosts))}
 
         # Symlink all the given common libs files or paths into the lib/ directory
-        for i in ${tomcat} ${toString cfg.commonLibs}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the common/lib/ directory
-                ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/lib/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
-                done
-            fi
+        for i in ${tomcat} ${toString cfg.commonLibs}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the common/lib/ directory
+            ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/lib/*; do
+              ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
+            done
+          fi
         done
 
         # Symlink all the given shared libs files or paths into the shared/lib/ directory
-        for i in ${toString cfg.sharedLibs}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the common/lib/ directory
-                ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/shared/lib/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
-                done
-            fi
+        for i in ${toString cfg.sharedLibs}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the common/lib/ directory
+            ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/shared/lib/*; do
+              ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
+            done
+          fi
         done
 
         # Symlink all the given web applications files or paths into the webapps/ directory
-        for i in ${toString cfg.webapps}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the webapps/ directory
-                ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/webapps/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
-                done
+        for i in ${toString cfg.webapps}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the webapps/ directory
+            ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/webapps/*; do
+              ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
+            done
 
-                # Also symlink the configuration files if they are included
-                if [ -d $i/conf/Catalina ]
-                then
-                    for j in $i/conf/Catalina/*
-                    do
-                        mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
-                        ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
-                    done
-                fi
+            # Also symlink the configuration files if they are included
+            if [ -d $i/conf/Catalina ]; then
+              for j in $i/conf/Catalina/*; do
+                mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
+                ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+              done
             fi
+          fi
         done
 
         ${toString (map (virtualHost: ''
@@ -275,94 +292,79 @@ in
 
           # Symlink all the given web applications files or paths into the webapps/ directory
           # of this virtual host
-          for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"
-          do
-              if [ -f $i ]
-              then
-                  # If the given web application is a file, symlink it into the webapps/ directory
-                  ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
-              elif [ -d $i ]
-              then
-                  # If the given web application is a directory, then iterate over the files
-                  # in the special purpose directories and symlink them into the tomcat tree
-
-                  for j in $i/webapps/*
-                  do
-                      ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
-                  done
-
-                  # Also symlink the configuration files if they are included
-                  if [ -d $i/conf/Catalina ]
-                  then
-                      for j in $i/conf/Catalina/*
-                      do
-                          mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
-                          ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
-                      done
-                  fi
+          for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"; do
+            if [ -f $i ]; then
+              # If the given web application is a file, symlink it into the webapps/ directory
+              ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
+            elif [ -d $i ]; then
+              # If the given web application is a directory, then iterate over the files
+              # in the special purpose directories and symlink them into the tomcat tree
+
+              for j in $i/webapps/*; do
+                ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
+              done
+
+              # Also symlink the configuration files if they are included
+              if [ -d $i/conf/Catalina ]; then
+                for j in $i/conf/Catalina/*; do
+                  mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
+                  ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
+                done
               fi
+            fi
           done
-
-          ''
-        ) cfg.virtualHosts) }
-
-        # Create a work/ directory
-        mkdir -p ${cfg.baseDir}/work
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/work
-
-        ${if cfg.axis2.enable then
-            ''
-            # Copy the Axis2 web application
-            cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
-
-            # Turn off addressing, which causes many errors
-            sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
-
-            # Modify permissions on the Axis2 application
-            chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
-
-            # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
-            for i in ${toString cfg.axis2.services}
-            do
-                if [ -f $i ]
-                then
-                    # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
-                    ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
-                elif [ -d $i ]
-                then
-                    # If the given web application is a directory, then iterate over the files
-                    # in the special purpose directories and symlink them into the tomcat tree
-
-                    for j in $i/webapps/axis2/WEB-INF/services/*
-                    do
-                        ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
-                    done
-
-                    # Also symlink the configuration files if they are included
-                    if [ -d $i/conf/Catalina ]
-                    then
-                        for j in $i/conf/Catalina/*
-                        do
-                            ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
-                        done
-                    fi
-                fi
-            done
-            ''
-        else ""}
-      '';
-
-      script = ''
-          ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh'
-      '';
-
-      preStop = ''
-        echo "Stopping tomcat..."
-        CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh
+        '') cfg.virtualHosts)}
+
+        ${optionalString cfg.axis2.enable ''
+          # Copy the Axis2 web application
+          cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
+
+          # Turn off addressing, which causes many errors
+          sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
+
+          # Modify permissions on the Axis2 application
+          chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
+
+          # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
+          for i in ${toString cfg.axis2.services}; do
+            if [ -f $i ]; then
+              # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
+              ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
+            elif [ -d $i ]; then
+              # If the given web application is a directory, then iterate over the files
+              # in the special purpose directories and symlink them into the tomcat tree
+
+              for j in $i/webapps/axis2/WEB-INF/services/*; do
+                ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
+              done
+
+              # Also symlink the configuration files if they are included
+              if [ -d $i/conf/Catalina ]; then
+                for j in $i/conf/Catalina/*; do
+                  ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+                done
+              fi
+            fi
+          done
+        ''}
       '';
 
+      serviceConfig = {
+        Type = "forking";
+        PermissionsStartOnly = true;
+        PIDFile="/run/tomcat/tomcat.pid";
+        RuntimeDirectory = "tomcat";
+        User = cfg.user;
+        Environment=[
+          "CATALINA_BASE=${cfg.baseDir}"
+          "CATALINA_PID=/run/tomcat/tomcat.pid"
+          "JAVA_HOME='${cfg.jdk}'"
+          "JAVA_OPTS='${builtins.toString cfg.javaOpts}'"
+          "CATALINA_OPTS='${builtins.toString cfg.catalinaOpts}'"
+        ] ++ cfg.extraEnvironment;
+        ExecStart = "${tomcat}/bin/startup.sh";
+        ExecStop = "${tomcat}/bin/shutdown.sh";
+      };
     };
-
   };
-
 }
diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix
index 4ede4fc20967..b6c7fef21fb2 100644
--- a/nixos/modules/services/web-servers/traefik.nix
+++ b/nixos/modules/services/web-servers/traefik.nix
@@ -64,6 +64,16 @@ in {
       '';
     };
 
+    group = mkOption {
+      default = "traefik";
+      type = types.string;
+      example = "docker";
+      description = ''
+        Set the group that traefik runs under.
+        For the docker backend this needs to be set to <literal>docker</literal> instead.
+      '';
+    };
+
     package = mkOption {
       default = pkgs.traefik;
       defaultText = "pkgs.traefik";
@@ -87,7 +97,7 @@ in {
         ];
         Type = "simple";
         User = "traefik";
-        Group = "traefik";
+        Group = cfg.group;
         Restart = "on-failure";
         StartLimitInterval = 86400;
         StartLimitBurst = 5;
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
index c3bc065d4651..bc74d62b116a 100644
--- a/nixos/modules/services/web-servers/varnish/default.nix
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -1,14 +1,27 @@
 { config, lib, pkgs, ...}:
+
+with lib;
+
 let
   cfg = config.services.varnish;
 
+  commandLine = "-f ${pkgs.writeText "default.vcl" cfg.config}" +
+      optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([cfg.package] ++ cfg.extraModules)}' -r vmod_path";
 in
-with lib;
 {
   options = {
     services.varnish = {
       enable = mkEnableOption "Varnish Server";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.varnish5;
+        defaultText = "pkgs.varnish5";
+        description = ''
+          The package to use
+        '';
+      };
+
       http_address = mkOption {
         type = types.str;
         default = "*:6081";
@@ -35,7 +48,7 @@ with lib;
       extraModules = mkOption {
         type = types.listOf types.package;
         default = [];
-        example = literalExample "[ pkgs.varnish-geoip ]";
+        example = literalExample "[ pkgs.varnish5Packages.geoip ]";
         description = "
           Varnish modules (except 'std').
         ";
@@ -69,8 +82,7 @@ with lib;
       serviceConfig = {
         Type = "simple";
         PermissionsStartOnly = true;
-        ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -F ${cfg.extraCommandLine}"
-          + optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([pkgs.varnish] ++ cfg.extraModules)}' -r vmod_path";
+        ExecStart = "${cfg.package}/sbin/varnishd -a ${cfg.http_address} -n ${cfg.stateDir} -F ${cfg.extraCommandLine} ${commandLine}";
         Restart = "always";
         RestartSec = "5s";
         User = "varnish";
@@ -81,7 +93,15 @@ with lib;
       };
     };
 
-    environment.systemPackages = [ pkgs.varnish ];
+    environment.systemPackages = [ cfg.package ];
+
+    # check .vcl syntax at compile time (e.g. before nixops deployment)
+    system.extraDependencies = [
+      (pkgs.stdenv.mkDerivation {
+        name = "check-varnish-syntax";
+        buildCommand = "${cfg.package}/sbin/varnishd -C ${commandLine} 2> $out || (cat $out; exit 1)";
+      })
+    ];
 
     users.extraUsers.varnish = {
       group = "varnish";
diff --git a/nixos/modules/services/x11/compton.nix b/nixos/modules/services/x11/compton.nix
index 11cbcac6fa85..8641c05de52e 100644
--- a/nixos/modules/services/x11/compton.nix
+++ b/nixos/modules/services/x11/compton.nix
@@ -181,10 +181,10 @@ in {
     };
 
     backend = mkOption {
-      type = types.enum [ "glx" "xrender" ];
+      type = types.enum [ "glx" "xrender" "xr_glx_hybrid" ];
       default = "xrender";
       description = ''
-        Backend to use: <literal>glx</literal> or <literal>xrender</literal>.
+        Backend to use: <literal>glx</literal>, <literal>xrender</literal> or <literal>xr_glx_hybrid</literal>.
       '';
     };
 
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 39b27d4ceb61..f435e85f6b83 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -87,11 +87,11 @@ in
 
       default = mkOption {
         type = types.str;
-        default = "none";
-        example = "plasma5";
+        default = "";
+        example = "none";
         description = "Default desktop manager loaded if none have been chosen.";
         apply = defaultDM:
-          if defaultDM == "none" && cfg.session.list != [] then
+          if defaultDM == "" && cfg.session.list != [] then
             (head cfg.session.list).name
           else if any (w: w.name == defaultDM) cfg.session.list then
             defaultDM
@@ -109,9 +109,5 @@ in
 
   };
 
-  config = {
-    services.xserver.displayManager.session = cfg.session.list;
-    environment.systemPackages =
-      mkIf cfg.session.needBGPackages [ pkgs.feh ]; # xsetroot via xserver.enable
-  };
+  config.services.xserver.displayManager.session = cfg.session.list;
 }
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 8a523f0d8036..da3287aaea6e 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -33,7 +33,7 @@ in
       pkgs.xorg.xauth # used by kdesu
       pkgs.gtk2 # To get GTK+'s themes.
       pkgs.tango-icon-theme
-      pkgs.shared_mime_info
+      pkgs.shared-mime-info
       pkgs.gnome2.gnomeicontheme
       pkgs.xorg.xcursorthemes
     ];
@@ -47,7 +47,7 @@ in
         export GTK_DATA_PREFIX=${config.system.path}
         # find theme engines
         export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
-        export XDG_MENU_PREFIX=enlightenment
+        export XDG_MENU_PREFIX=e-
 
         export GST_PLUGIN_PATH="${GST_PLUGIN_PATH}"
 
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index e5a79496c7a5..10e8ef0ed381 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -27,7 +27,7 @@ let
   nixos-gsettings-desktop-schemas = pkgs.runCommand "nixos-gsettings-desktop-schemas" {}
     ''
      mkdir -p $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
-     cp -rf ${pkgs.gnome3.gsettings_desktop_schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
+     cp -rf ${pkgs.gnome3.gsettings-desktop-schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
 
      ${concatMapStrings (pkg: "cp -rf ${pkg}/share/gsettings-schemas/*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas\n") cfg.extraGSettingsOverridePackages}
 
@@ -60,7 +60,7 @@ in {
         example = literalExample "[ pkgs.gnome3.gpaste ]";
         description = "Additional list of packages to be added to the session search path.
                        Useful for gnome shell extensions or gsettings-conditionated autostart.";
-        apply = list: list ++ [ pkgs.gnome3.gnome_shell pkgs.gnome3.gnome-shell-extensions ];
+        apply = list: list ++ [ pkgs.gnome3.gnome-shell pkgs.gnome3.gnome-shell-extensions ];
       };
 
       extraGSettingsOverrides = mkOption {
@@ -108,6 +108,7 @@ in {
     services.gnome3.seahorse.enable = mkDefault true;
     services.gnome3.sushi.enable = mkDefault true;
     services.gnome3.tracker.enable = mkDefault true;
+    services.gnome3.tracker-miners.enable = mkDefault true;
     hardware.pulseaudio.enable = mkDefault true;
     services.telepathy.enable = mkDefault true;
     networking.networkmanager.enable = mkDefault true;
@@ -117,13 +118,13 @@ in {
     services.packagekit.enable = mkDefault true;
     hardware.bluetooth.enable = mkDefault true;
     services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
-    services.udev.packages = [ pkgs.gnome3.gnome_settings_daemon ];
+    services.udev.packages = [ pkgs.gnome3.gnome-settings-daemon ];
     systemd.packages = [ pkgs.gnome3.vino ];
 
     # If gnome3 is installed, build vim for gtk3 too.
     nixpkgs.config.vim.gui = "gtk3";
 
-    fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell_fonts ];
+    fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell-fonts ];
 
     services.xserver.desktopManager.session = singleton
       { name = "gnome3";
@@ -135,7 +136,7 @@ in {
           # find theme engines
           export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
 
-          export XDG_MENU_PREFIX=gnome
+          export XDG_MENU_PREFIX=gnome-
 
           ${concatMapStrings (p: ''
             if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then
@@ -152,7 +153,7 @@ in {
           export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${mimeAppsList}/share
 
           # Override gsettings-desktop-schema
-          export XDG_DATA_DIRS=${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides''${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS
+          export NIX_GSETTINGS_OVERRIDES_DIR=${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
 
           # Let nautilus find extensions
           export NAUTILUS_EXTENSION_DIR=${config.system.path}/lib/nautilus/extensions-3.0/
@@ -163,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
 
-          ${pkgs.gnome3.gnome_session}/bin/gnome-session ${optionalString cfg.debug "--debug"} &
+          ${pkgs.gnome3.gnome-session}/bin/gnome-session ${optionalString cfg.debug "--debug"} &
           waitPID=$!
         '';
       };
@@ -171,7 +172,7 @@ in {
     services.xserver.updateDbusEnvironment = true;
 
     environment.variables.GIO_EXTRA_MODULES = [ "${lib.getLib pkgs.gnome3.dconf}/lib/gio/modules"
-                                                "${pkgs.gnome3.glib_networking.out}/lib/gio/modules"
+                                                "${pkgs.gnome3.glib-networking.out}/lib/gio/modules"
                                                 "${pkgs.gnome3.gvfs}/lib/gio/modules" ];
     environment.systemPackages = pkgs.gnome3.corePackages ++ cfg.sessionPath
       ++ (removePackagesByName pkgs.gnome3.optionalPackages config.environment.gnome3.excludePackages);
@@ -179,10 +180,9 @@ in {
     # Use the correct gnome3 packageSet
     networking.networkmanager.basePackages =
       { inherit (pkgs) networkmanager modemmanager wpa_supplicant;
-        inherit (pkgs.gnome3) networkmanager_openvpn networkmanager_vpnc
-                              networkmanager_openconnect networkmanager_fortisslvpn
-                              networkmanager_pptp networkmanager_iodine
-                              networkmanager_l2tp; };
+        inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
+                              networkmanager-openconnect networkmanager-fortisslvpn
+                              networkmanager-iodine networkmanager-l2tp; };
 
     # Needed for themes and backgrounds
     environment.pathsToLink = [ "/share" ];
diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix
index fb907618d35b..2596ec4ad85c 100644
--- a/nixos/modules/services/x11/desktop-managers/lxqt.nix
+++ b/nixos/modules/services/x11/desktop-managers/lxqt.nix
@@ -61,6 +61,8 @@ in
 
     environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.gvfs}/lib/gio/modules" ];
 
+    services.upower.enable = config.powerManagement.enable;
   };
 
+
 }
diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix
index ab8a0a48b483..db83aaf3c19f 100644
--- a/nixos/modules/services/x11/desktop-managers/mate.nix
+++ b/nixos/modules/services/x11/desktop-managers/mate.nix
@@ -12,6 +12,17 @@ let
     in
       filter (x: !(builtins.elem (pkgName x) ysNames)) xs;
 
+  addToXDGDirs = p: ''
+    if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then
+      export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name}
+    fi
+
+    if [ -d "${p}/lib/girepository-1.0" ]; then
+      export GI_TYPELIB_PATH=$GI_TYPELIB_PATH''${GI_TYPELIB_PATH:+:}${p}/lib/girepository-1.0
+      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib
+    fi
+  '';
+
   xcfg = config.services.xserver;
   cfg = xcfg.desktopManager.mate;
 
@@ -20,10 +31,14 @@ in
 {
   options = {
 
-    services.xserver.desktopManager.mate.enable = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Enable the MATE desktop environment";
+    services.xserver.desktopManager.mate = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the MATE desktop environment";
+      };
+
+      debug = mkEnableOption "mate-session debug messages";
     };
 
     environment.mate.excludePackages = mkOption {
@@ -47,15 +62,34 @@ in
         # Find theme engines
         export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
 
-        export XDG_MENU_PREFIX=mate
+        export XDG_MENU_PREFIX=mate-
 
         # Find the mouse
         export XCURSOR_PATH=~/.icons:${config.system.path}/share/icons
 
+        # Let caja find extensions
+        export CAJA_EXTENSION_DIRS=$CAJA_EXTENSION_DIRS''${CAJA_EXTENSION_DIRS:+:}${config.system.path}/lib/caja/extensions-2.0
+
+        # Let caja extensions find gsettings schemas
+        ${concatMapStrings (p: ''
+          if [ -d "${p}/lib/caja/extensions-2.0" ]; then
+            ${addToXDGDirs p}
+          fi
+          '')
+          config.environment.systemPackages
+        }
+
+        # Let mate-panel find applets
+        export MATE_PANEL_APPLETS_DIR=$MATE_PANEL_APPLETS_DIR''${MATE_PANEL_APPLETS_DIR:+:}${config.system.path}/share/mate-panel/applets
+        export MATE_PANEL_EXTRA_MODULES=$MATE_PANEL_EXTRA_MODULES''${MATE_PANEL_EXTRA_MODULES:+:}${config.system.path}/lib/mate-panel/applets
+
+        # Add mate-control-center paths to some XDG variables because its schemas are needed by mate-settings-daemon, and mate-settings-daemon is a dependency for mate-control-center (that is, they are mutually recursive)
+        ${addToXDGDirs pkgs.mate.mate-control-center}
+
         # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
         ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
 
-        ${pkgs.mate.mate-session-manager}/bin/mate-session &
+        ${pkgs.mate.mate-session-manager}/bin/mate-session ${optionalString cfg.debug "--debug"} &
         waitPID=$!
       '';
     };
@@ -68,12 +102,14 @@ in
 
     services.dbus.packages = [
       pkgs.gnome3.dconf
-      pkgs.at_spi2_core
+      pkgs.at-spi2-core
     ];
 
     services.gnome3.gnome-keyring.enable = true;
     services.upower.enable = config.powerManagement.enable;
 
+    security.pam.services."mate-screensaver".unixAuth = true;
+
     environment.pathsToLink = [ "/share" ];
   };
 
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index bb4f4e868fea..91d091d7d7e2 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -25,8 +25,8 @@ in
         type = types.bool;
         default = true;
         description = ''
-          Enable support for Qt 4-based applications. Particularly, install the
-          Qt 4 version of the Breeze theme and a default backend for Phonon.
+          Enable support for Qt 4-based applications. Particularly, install a
+          default backend for Phonon.
         '';
       };
 
@@ -47,6 +47,18 @@ in
             ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1"
           ''}
 
+          if [ -f "$HOME/.config/kdeglobals" ]
+          then
+              # Remove extraneous font style names.
+              # See also: https://phabricator.kde.org/D9070
+              ${getBin pkgs.gnused}/bin/sed -i "$HOME/.config/kdeglobals" \
+                  -e '/^fixed=/ s/,Regular$//' \
+                  -e '/^font=/ s/,Regular$//' \
+                  -e '/^menuFont=/ s/,Regular$//' \
+                  -e '/^smallestReadableFont=/ s/,Regular$//' \
+                  -e '/^toolBarFont=/ s/,Regular$//'
+          fi
+
           exec "${getBin plasma5.plasma-workspace}/bin/startkde"
         '';
       };
@@ -54,6 +66,10 @@ in
       security.wrappers = {
         kcheckpass.source = "${lib.getBin plasma5.plasma-workspace}/lib/libexec/kcheckpass";
         "start_kdeinit".source = "${lib.getBin pkgs.kinit}/lib/libexec/kf5/start_kdeinit";
+        kwin_wayland = {
+          source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
+          capabilities = "cap_sys_nice+ep";
+        };
       };
 
       environment.systemPackages = with pkgs; with qt5; with libsForQt5; with plasma5; with kdeApplications;
@@ -138,15 +154,17 @@ in
           print-manager
 
           breeze-icons
-          pkgs.hicolor_icon_theme
+          pkgs.hicolor-icon-theme
 
           kde-gtk-config breeze-gtk
 
+          qtvirtualkeyboard
+
           libsForQt56.phonon-backend-gstreamer
           libsForQt5.phonon-backend-gstreamer
         ]
 
-        ++ lib.optionals cfg.enableQt4Support [ breeze-qt4 pkgs.phonon-backend-gstreamer ]
+        ++ lib.optionals cfg.enableQt4Support [ pkgs.phonon-backend-gstreamer ]
 
         # Optional hardware support features
         ++ lib.optional config.hardware.bluetooth.enable bluedevil
@@ -193,16 +211,6 @@ in
         theme = mkDefault "breeze";
       };
 
-      boot.plymouth = {
-        theme = mkDefault "breeze";
-        themePackages = mkDefault [
-          (pkgs.breeze-plymouth.override {
-            nixosBranding = true;
-            nixosVersion = config.system.nixosRelease;
-          })
-        ];
-      };
-
       security.pam.services.kde = { allowNullPassword = true; };
 
       # Doing these one by one seems silly, but we currently lack a better
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 9c42dc8781b9..7dcc600d2664 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -3,15 +3,11 @@
 with lib;
 
 let
-
-  xcfg = config.services.xserver;
-  cfg = xcfg.desktopManager.xfce;
-
+  cfg = config.services.xserver.desktopManager.xfce;
 in
 
 {
   options = {
-
     services.xserver.desktopManager.xfce = {
       enable = mkOption {
         type = types.bool;
@@ -54,81 +50,93 @@ in
         description = "Application used by XFCE to lock the screen.";
       };
     };
-
   };
 
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs.xfce // pkgs; [
+      # Get GTK+ themes and gtk-update-icon-cache
+      gtk2.out
+
+      # Supplies some abstract icons such as:
+      # utilities-terminal, accessories-text-editor
+      gnome3.defaultIconTheme
+
+      hicolor-icon-theme
+      tango-icon-theme
+      xfce4-icon-theme
+
+      desktop-file-utils
+      shared-mime-info
+
+      # Needed by Xfce's xinitrc script
+      # TODO: replace with command -v
+      which
+
+      exo
+      garcon
+      gtk-xfce-engine
+      gvfs
+      libxfce4ui
+      tumbler
+      xfconf
+
+      mousepad
+      ristretto
+      xfce4-appfinder
+      xfce4-screenshooter
+      xfce4-session
+      xfce4-settings
+      xfce4-terminal
+
+      (thunar.override { thunarPlugins = cfg.thunarPlugins; })
+      thunar-volman # TODO: drop
+    ] ++ (if config.hardware.pulseaudio.enable
+          then [ xfce4-mixer-pulse xfce4-volumed-pulse ]
+	  else [ xfce4-mixer xfce4-volumed ])
+      # TODO: NetworkManager doesn't belong here
+      ++ optionals config.networking.networkmanager.enable [ networkmanagerapplet ]
+      ++ optionals config.powerManagement.enable [ xfce4-power-manager ]
+      ++ optionals cfg.enableXfwm [ xfwm4 ]
+      ++ optionals (!cfg.noDesktop) [
+        xfce4-panel
+        xfce4-notifyd
+        xfdesktop
+      ];
+
+    environment.pathsToLink = [
+      "/share/xfce4"
+      "/share/themes"
+      "/share/mime"
+      "/share/desktop-directories"
+      "/share/gtksourceview-2.0"
+    ];
+
+    environment.variables = {
+      GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
+      GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
+    };
 
-  config = mkIf (xcfg.enable && cfg.enable) {
-
-    services.xserver.desktopManager.session = singleton
-      { name = "xfce";
-        bgSupport = true;
-        start =
-          ''
-            ${cfg.extraSessionCommands}
+    services.xserver.desktopManager.session = [{
+      name = "xfce";
+      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"
+        # 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"
 
-            # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
-            export GTK_DATA_PREFIX=${config.system.path}
+        # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
+        export GTK_DATA_PREFIX=${config.system.path}
 
-            ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} &
-            waitPID=$!
-          '';
-      };
+        ${pkgs.runtimeShell} ${pkgs.xfce.xinitrc} &
+        waitPID=$!
+      '';
+    }];
 
     services.xserver.updateDbusEnvironment = true;
 
-    environment.systemPackages =
-      [ pkgs.gtk2.out # To get GTK+'s themes and gtk-update-icon-cache
-        pkgs.hicolor_icon_theme
-        pkgs.tango-icon-theme
-        pkgs.shared_mime_info
-        pkgs.which # Needed by the xfce's xinitrc script.
-        pkgs."${cfg.screenLock}"
-        pkgs.xfce.exo
-        pkgs.xfce.gtk_xfce_engine
-        pkgs.xfce.mousepad
-        pkgs.xfce.ristretto
-        pkgs.xfce.terminal
-       (pkgs.xfce.thunar.override { thunarPlugins = cfg.thunarPlugins; })
-        pkgs.xfce.xfce4icontheme
-        pkgs.xfce.xfce4session
-        pkgs.xfce.xfce4settings
-        pkgs.xfce.xfce4mixer
-        pkgs.xfce.xfce4volumed
-        pkgs.xfce.xfce4-screenshooter
-        pkgs.xfce.xfconf
-        # This supplies some "abstract" icons such as
-        # "utilities-terminal" and "accessories-text-editor".
-        pkgs.gnome3.defaultIconTheme
-        pkgs.desktop_file_utils
-        pkgs.xfce.libxfce4ui
-        pkgs.xfce.garcon
-        pkgs.xfce.thunar_volman
-        pkgs.xfce.gvfs
-        pkgs.xfce.xfce4_appfinder
-        pkgs.xfce.tumbler       # found via dbus
-      ]
-      ++ optional cfg.enableXfwm pkgs.xfce.xfwm4
-      ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager
-      ++ optional config.networking.networkmanager.enable pkgs.networkmanagerapplet
-      ++ optionals (!cfg.noDesktop)
-         [ pkgs.xfce.xfce4panel
-           pkgs.xfce.xfdesktop
-	   pkgs.xfce.xfce4notifyd  # found via dbus
-         ];
-
-    environment.pathsToLink =
-      [ "/share/xfce4" "/share/themes" "/share/mime" "/share/desktop-directories" "/share/gtksourceview-2.0" ];
-
-    environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
-
     # Enable helpful DBus services.
     services.udisks2.enable = true;
     services.upower.enable = config.powerManagement.enable;
-
   };
-
 }
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 3fa482fb6722..43ed21c95fee 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -59,12 +59,6 @@ let
       # Now it should be safe to assume that the script was called with the
       # expected parameters.
 
-      ${optionalString cfg.displayManager.logToJournal ''
-        if [ -z "$_DID_SYSTEMD_CAT" ]; then
-          _DID_SYSTEMD_CAT=1 exec ${config.systemd.package}/bin/systemd-cat -t xsession -- "$0" "$@"
-        fi
-      ''}
-
       . /etc/profile
       cd "$HOME"
 
@@ -72,16 +66,23 @@ let
       sessionType="$1"
       if [ "$sessionType" = default ]; then sessionType=""; fi
 
-      ${optionalString (!cfg.displayManager.job.logsXsession && !cfg.displayManager.logToJournal) ''
-        exec > ~/.xsession-errors 2>&1
-      ''}
-
       ${optionalString cfg.startDbusSession ''
         if test -z "$DBUS_SESSION_BUS_ADDRESS"; then
           exec ${pkgs.dbus.dbus-launch} --exit-with-session "$0" "$sessionType"
         fi
       ''}
 
+      ${optionalString cfg.displayManager.job.logToJournal ''
+        if [ -z "$_DID_SYSTEMD_CAT" ]; then
+          export _DID_SYSTEMD_CAT=1
+          exec ${config.systemd.package}/bin/systemd-cat -t xsession "$0" "$sessionType"
+        fi
+      ''}
+
+      ${optionalString cfg.displayManager.job.logToFile ''
+        exec &> >(tee ~/.xsession-errors)
+      ''}
+
       # Start PulseAudio if enabled.
       ${optionalString (config.hardware.pulseaudio.enable) ''
         ${optionalString (!config.hardware.pulseaudio.systemWide)
@@ -306,26 +307,24 @@ in
           description = "Additional environment variables needed by the display manager.";
         };
 
-        logsXsession = mkOption {
+        logToFile = mkOption {
           type = types.bool;
           default = false;
           description = ''
-            Whether the display manager redirects the
-            output of the session script to
-            <filename>~/.xsession-errors</filename>.
+            Whether the display manager redirects the output of the
+            session script to <filename>~/.xsession-errors</filename>.
           '';
         };
 
-      };
+        logToJournal = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether the display manager redirects the output of the
+            session script to the systemd journal.
+          '';
+        };
 
-      logToJournal = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          By default, the stdout/stderr of sessions is written
-          to <filename>~/.xsession-errors</filename>. When this option
-          is enabled, it will instead be written to the journal.
-        '';
       };
 
     };
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index e83f26516f5f..70fc7388c2ac 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -122,11 +122,8 @@ in
       "rc-local.service"
       "systemd-machined.service"
       "systemd-user-sessions.service"
-      "getty@tty1.service"
     ];
 
-    systemd.services."getty@tty1".enable = false;
-    systemd.services.display-manager.conflicts = [ "getty@tty1.service" ];
     systemd.services.display-manager.serviceConfig = {
       # Restart = "always"; - already defined in xserver.nix
       KillMode = "mixed";
@@ -136,7 +133,7 @@ in
       StandardError = "inherit";
     };
 
-    systemd.services.display-manager.path = [ pkgs.gnome3.gnome_session ];
+    systemd.services.display-manager.path = [ pkgs.gnome3.gnome-session ];
 
     services.dbus.packages = [ gdm ];
 
@@ -196,7 +193,7 @@ in
         auth     required       pam_env.so envfile=${config.system.build.pamEnvironment}
 
         auth     required       pam_succeed_if.so uid >= 1000 quiet
-        auth     optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so
+        auth     optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so
         auth     ${if config.security.pam.enableEcryptfs then "required" else "sufficient"} pam_unix.so nullok likeauth
         ${optionalString config.security.pam.enableEcryptfs
           "auth required ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
@@ -216,7 +213,7 @@ in
           "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
         session  required       pam_loginuid.so
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
-        session  optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so auto_start
+        session  optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start
       '';
 
       gdm-password.text = ''
@@ -224,7 +221,7 @@ in
         auth     required       pam_env.so envfile=${config.system.build.pamEnvironment}
 
         auth     required       pam_succeed_if.so uid >= 1000 quiet
-        auth     optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so
+        auth     optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so
         auth     ${if config.security.pam.enableEcryptfs then "required" else "sufficient"} pam_unix.so nullok likeauth
         ${optionalString config.security.pam.enableEcryptfs
           "auth required ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
@@ -243,7 +240,7 @@ in
           "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
         session  required       pam_loginuid.so
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
-        session  optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so auto_start
+        session  optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start
       '';
 
       gdm-autologin.text = ''
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
index 1d5dcb2c7cbe..2a71d2338607 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
@@ -45,6 +45,8 @@ let
     theme-name = ${cfg.theme.name}
     icon-theme-name = ${cfg.iconTheme.name}
     background = ${ldmcfg.background}
+    ${optionalString (cfg.clock-format != null) "clock-format = ${cfg.clock-format}"}
+    ${optionalString (cfg.indicators != null) "indicators = ${concatStringsSep ";" cfg.indicators}"}
     ${cfg.extraConfig}
     '';
 
@@ -66,8 +68,8 @@ in
 
         package = mkOption {
           type = types.package;
-          default = pkgs.gnome3.gnome_themes_standard;
-          defaultText = "pkgs.gnome3.gnome_themes_standard";
+          default = pkgs.gnome3.gnome-themes-standard;
+          defaultText = "pkgs.gnome3.gnome-themes-standard";
           description = ''
             The package path that contains the theme given in the name option.
           '';
@@ -104,6 +106,35 @@ in
 
       };
 
+      clock-format = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "%F";
+        description = ''
+          Clock format string (as expected by strftime, e.g. "%H:%M")
+          to use with the lightdm gtk greeter panel.
+
+          If set to null the default clock format is used.
+        '';
+      };
+
+      indicators = mkOption {
+        type = types.nullOr (types.listOf types.str);
+        default = null;
+        example = [ "~host" "~spacer" "~clock" "~spacer" "~session" "~language" "~a11y" "~power" ];
+        description = ''
+          List of allowed indicator modules to use for the lightdm gtk
+          greeter panel.
+
+          Built-in indicators include "~a11y", "~language", "~session",
+          "~power", "~clock", "~host", "~spacer". Unity indicators can be
+          represented by short name (e.g. "sound", "power"), service file name,
+          or absolute path.
+
+          If set to null the default indicators are used.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 1733f2fd39b2..9d30155a7234 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -9,6 +9,10 @@ let
   xEnv = config.systemd.services."display-manager".environment;
   cfg = dmcfg.lightdm;
 
+  dmDefault = xcfg.desktopManager.default;
+  wmDefault = xcfg.windowManager.default;
+  hasDefaultUserSession = dmDefault != "none" || wmDefault != "none";
+
   inherit (pkgs) stdenv lightdm writeScript writeText;
 
   # lightdm runs with clearenv(), but we need a few things in the enviornment for X to startup
@@ -54,14 +58,13 @@ let
         autologin-user-timeout = ${toString cfg.autoLogin.timeout}
         autologin-session = ${defaultSessionName}
       ''}
+      ${optionalString hasDefaultUserSession ''
+        user-session=${defaultSessionName}
+      ''}
       ${cfg.extraSeatDefaults}
     '';
 
-  defaultSessionName =
-    let
-      dm = xcfg.desktopManager.default;
-      wm = xcfg.windowManager.default;
-    in dm + optionalString (wm != "none") ("+" + wm);
+  defaultSessionName = dmDefault + optionalString (wmDefault != "none") ("+" + wmDefault);
 in
 {
   # Note: the order in which lightdm greeter modules are imported
@@ -179,6 +182,14 @@ in
           default session: ${defaultSessionName} is not valid.
         '';
       }
+      { assertion = hasDefaultUserSession -> elem defaultSessionName dmcfg.session.names;
+        message = ''
+          services.xserver.desktopManager.default and
+          services.xserver.windowMananger.default are not set to valid
+          values. The current default session: ${defaultSessionName}
+          is not valid.
+        '';
+      }
       { assertion = !cfg.greeter.enable -> (cfg.autoLogin.enable && cfg.autoLogin.timeout == 0);
         message = ''
           LightDM can only run without greeter if automatic login is enabled and the timeout for it
@@ -190,7 +201,7 @@ in
     services.xserver.displayManager.slim.enable = false;
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
       execCmd = ''
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index facaea131ae5..2d4cb8aa20a5 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -205,7 +205,7 @@ in
     services.xserver.displayManager.slim.enable = false;
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       environment = {
         # Load themes from system environment
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index 0c4dd1973b53..f645a5c2f078 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -14,7 +14,7 @@ let
       default_xserver ${dmcfg.xserverBin}
       xserver_arguments ${toString dmcfg.xserverArgs}
       sessiondir ${dmcfg.session.desktops}
-      login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session"
+      login_cmd exec ${pkgs.runtimeShell} ${dmcfg.session.script} "%session"
       halt_cmd ${config.systemd.package}/sbin/shutdown -h now
       reboot_cmd ${config.systemd.package}/sbin/shutdown -r now
       logfile /dev/stderr
diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix
index 8f5ce3dccc6a..b46ede550c16 100644
--- a/nixos/modules/services/x11/display-managers/xpra.nix
+++ b/nixos/modules/services/x11/display-managers/xpra.nix
@@ -220,7 +220,7 @@ in
     '';
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       execCmd = ''
         ${optionalString (cfg.pulseaudio)
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index 44555cb6e2a9..d0a87f183b6f 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -170,7 +170,7 @@ in {
 
       disableWhileTyping = mkOption {
         type = types.bool;
-        default = true;
+        default = false;
         description =
           ''
             Disable input method while typing.
@@ -198,6 +198,13 @@ in {
 
     environment.systemPackages = [ pkgs.xorg.xf86inputlibinput ];
 
+    environment.etc = [
+      (let cfgPath = "X11/xorg.conf.d/40-libinput.conf"; in {
+        source = pkgs.xorg.xf86inputlibinput.out + "/share/" + cfgPath;
+        target = cfgPath;
+      })
+    ];
+
     services.udev.packages = [ pkgs.libinput ];
 
     services.xserver.config =
diff --git a/nixos/modules/services/x11/window-managers/2bwm.nix b/nixos/modules/services/x11/window-managers/2bwm.nix
index e3f5ec7dbe67..fdbdf35b0f5a 100644
--- a/nixos/modules/services/x11/window-managers/2bwm.nix
+++ b/nixos/modules/services/x11/window-managers/2bwm.nix
@@ -25,12 +25,12 @@ in
       { name = "2bwm";
         start =
           ''
-            ${pkgs."2bwm"}/bin/2bwm &
+            ${pkgs._2bwm}/bin/2bwm &
             waitPID=$!
           '';
       };
 
-    environment.systemPackages = [ pkgs."2bwm" ];
+    environment.systemPackages = [ pkgs._2bwm ];
 
   };
 
diff --git a/nixos/modules/services/x11/window-managers/awesome.nix b/nixos/modules/services/x11/window-managers/awesome.nix
index eb97449c6bd9..71eb02ec5954 100644
--- a/nixos/modules/services/x11/window-managers/awesome.nix
+++ b/nixos/modules/services/x11/window-managers/awesome.nix
@@ -6,7 +6,11 @@ let
 
   cfg = config.services.xserver.windowManager.awesome;
   awesome = cfg.package;
-  inherit (pkgs.luaPackages) getLuaPath getLuaCPath;
+  getLuaPath = lib : dir : "${lib}/${dir}/lua/${pkgs.luaPackages.lua.luaversion}";
+  makeSearchPath = lib.concatMapStrings (path:
+    " --search " + (getLuaPath path "share") +
+    " --search " + (getLuaPath path "lib")
+  );
 in
 
 {
@@ -46,10 +50,7 @@ in
       { name = "awesome";
         start =
           ''
-            export LUA_CPATH="${lib.concatStringsSep ";" (map getLuaCPath cfg.luaModules)}"
-            export LUA_PATH="${lib.concatStringsSep ";" (map getLuaPath cfg.luaModules)}"
-
-            ${awesome}/bin/awesome &
+            ${awesome}/bin/awesome ${makeSearchPath cfg.luaModules} &
             waitPID=$!
           '';
       };
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 25ba95fccd75..e617e55a7a57 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -12,6 +12,7 @@ in
     ./afterstep.nix
     ./bspwm.nix
     ./dwm.nix
+    ./evilwm.nix
     ./exwm.nix
     ./fluxbox.nix
     ./fvwm.nix
@@ -61,9 +62,7 @@ in
         example = "wmii";
         description = "Default window manager loaded if none have been chosen.";
         apply = defaultWM:
-          if defaultWM == "none" && cfg.session != []  then
-            (head cfg.session).name
-          else if any (w: w.name == defaultWM) cfg.session then
+          if any (w: w.name == defaultWM) cfg.session then
             defaultWM
           else
             throw "Default window manager (${defaultWM}) not found.";
diff --git a/nixos/modules/services/x11/window-managers/evilwm.nix b/nixos/modules/services/x11/window-managers/evilwm.nix
new file mode 100644
index 000000000000..6e19e3572c79
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/evilwm.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.windowManager.evilwm;
+in
+{
+  ###### interface
+  options = {
+    services.xserver.windowManager.evilwm.enable = mkEnableOption "evilwm";
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton {
+      name = "evilwm";
+      start = ''
+	${pkgs.evilwm}/bin/evilwm &
+	waitPID=$!
+      '';
+    };
+    environment.systemPackages = [ pkgs.evilwm ];
+  };
+}
diff --git a/nixos/modules/services/x11/xautolock.nix b/nixos/modules/services/x11/xautolock.nix
index 28fc92024bcb..a614559970e9 100644
--- a/nixos/modules/services/x11/xautolock.nix
+++ b/nixos/modules/services/x11/xautolock.nix
@@ -26,9 +26,9 @@ in
         };
 
         locker = mkOption {
-          default = "xlock"; # default according to `man xautolock`
-          example = "i3lock -i /path/to/img";
-          type = types.string;
+          default = "${pkgs.xlockmore}/bin/xlock"; # default according to `man xautolock`
+          example = "${pkgs.i3lock}/bin/i3lock -i /path/to/img";
+          type = types.str;
 
           description = ''
             The script to use when automatically locking the computer.
@@ -37,8 +37,8 @@ in
 
         nowlocker = mkOption {
           default = null;
-          example = "i3lock -i /path/to/img";
-          type = types.nullOr types.string;
+          example = "${pkgs.i3lock}/bin/i3lock -i /path/to/img";
+          type = types.nullOr types.str;
 
           description = ''
             The script to use when manually locking the computer with <command>xautolock -locknow</command>.
@@ -56,10 +56,8 @@ in
 
         notifier = mkOption {
           default = null;
-          example = literalExample ''
-            "${pkgs.libnotify}/bin/notify-send \"Locking in 10 seconds\""
-          '';
-          type = types.nullOr types.string;
+          example = "${pkgs.libnotify}/bin/notify-send \"Locking in 10 seconds\"";
+          type = types.nullOr types.str;
 
           description = ''
             Notification script to be used to warn about the pending autolock.
@@ -68,8 +66,8 @@ in
 
         killer = mkOption {
           default = null; # default according to `man xautolock` is none
-          example = "systemctl suspend";
-          type = types.nullOr types.string;
+          example = "${pkgs.systemd}/bin/systemctl suspend";
+          type = types.nullOr types.str;
 
           description = ''
             The script to use when nothing has happend for as long as <option>killtime</option>
@@ -131,6 +129,12 @@ in
           assertion = cfg.killer != null -> cfg.killtime >= 10;
           message = "killtime has to be at least 10 minutes according to `man xautolock`";
         }
-      ];
+      ] ++ (lib.flip map [ "locker" "notifier" "nowlocker" "killer" ]
+        (option:
+        {
+          assertion = cfg."${option}" != null -> builtins.substring 0 1 cfg."${option}" == "/";
+          message = "Please specify a canonical path for `services.xserver.xautolock.${option}`";
+        })
+      );
     };
   }
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 7d544e153e9a..5f0a0f278452 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -161,15 +161,6 @@ in
         '';
       };
 
-      plainX = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether the X11 session can be plain (without DM/WM) and
-          the Xsession script will be used as fallback or not.
-        '';
-      };
-
       autorun = mkOption {
         type = types.bool;
         default = true;
@@ -548,7 +539,7 @@ in
           knownVideoDrivers;
       in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver));
 
-    nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; };
+    nixpkgs.config = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { xorg.abiCompat = "1.18"; };
 
     assertions = [
       { assertion = config.security.polkit.enable;
@@ -561,11 +552,6 @@ in
                 + "${toString (length primaryHeads)} heads set to primary: "
                 + concatMapStringsSep ", " (x: x.output) primaryHeads;
       })
-      { assertion = cfg.desktopManager.default == "none" && cfg.windowManager.default == "none" -> cfg.plainX;
-        message = "Either the desktop manager or the window manager shouldn't be `none`! "
-                + "To explicitly allow this, you can also set `services.xserver.plainX` to `true`. "
-                + "The `default` value looks for enabled WMs/DMs and select the first one.";
-      }
     ];
 
     environment.etc =
@@ -578,6 +564,22 @@ in
             target = "X11/xkb";
           }
         ])
+      # localectl looks into 00-keyboard.conf
+      ++ [
+        {
+          text = ''
+            Section "InputClass"
+              Identifier "Keyboard catchall"
+              MatchIsKeyboard "on"
+              Option "XkbModel" "${cfg.xkbModel}"
+              Option "XkbLayout" "${cfg.layout}"
+              Option "XkbOptions" "${cfg.xkbOptions}"
+              Option "XkbVariant" "${cfg.xkbVariant}"
+            EndSection
+          '';
+          target = "X11/xorg.conf.d/00-keyboard.conf";
+        }
+      ]
       # Needed since 1.18; see https://bugs.freedesktop.org/show_bug.cgi?id=89023#c5
       ++ (let cfgPath = "/X11/xorg.conf.d/10-evdev.conf"; in
         [{
@@ -624,9 +626,7 @@ in
 
         environment =
           {
-            XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime.
-            LD_LIBRARY_PATH = concatStringsSep ":" (
-              [ "${xorg.libX11.out}/lib" "${xorg.libXext.out}/lib" "/run/opengl-driver/lib" ]
+            LD_LIBRARY_PATH = concatStringsSep ":" ([ "/run/opengl-driver/lib" ]
               ++ concatLists (catAttrs "libPath" cfg.drivers));
           } // cfg.displayManager.job.environment;
 
@@ -697,16 +697,6 @@ in
           ${cfg.monitorSection}
         EndSection
 
-        Section "InputClass"
-          Identifier "Keyboard catchall"
-          MatchIsKeyboard "on"
-          Option "XkbRules" "base"
-          Option "XkbModel" "${cfg.xkbModel}"
-          Option "XkbLayout" "${cfg.layout}"
-          Option "XkbOptions" "${cfg.xkbOptions}"
-          Option "XkbVariant" "${cfg.xkbVariant}"
-        EndSection
-
         # Additional "InputClass" sections
         ${flip concatMapStrings cfg.inputClassSections (inputClassSection: ''
         Section "InputClass"
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index c2ac731d433d..c563614caaaf 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -61,7 +61,7 @@ in
       apply = set: {
         script =
           ''
-            #! ${pkgs.stdenv.shell}
+            #! ${pkgs.runtimeShell}
 
             systemConfig=@out@
 
@@ -117,14 +117,7 @@ in
 
   config = {
 
-    system.activationScripts.stdio =
-      ''
-        # Needed by some programs.
-        ln -sfn /proc/self/fd /dev/fd
-        ln -sfn /proc/self/fd/0 /dev/stdin
-        ln -sfn /proc/self/fd/1 /dev/stdout
-        ln -sfn /proc/self/fd/2 /dev/stderr
-      '';
+    system.activationScripts.stdio = ""; # obsolete
 
     system.activationScripts.var =
       ''
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 29cc60b00324..87a4ab2a586d 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -16,6 +16,10 @@ my $reloadListFile = "/run/systemd/reload-list";
 
 my $action = shift @ARGV;
 
+if ("@localeArchive@" ne "") {
+    $ENV{LOCALE_ARCHIVE} = "@localeArchive@";
+}
+
 if (!defined $action || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) {
     print STDERR <<EOF;
 Usage: $0 [switch|boot|test]
@@ -65,7 +69,8 @@ $SIG{PIPE} = "IGNORE";
 sub getActiveUnits {
     # FIXME: use D-Bus or whatever to query this, since parsing the
     # output of list-units is likely to break.
-    my $lines = `LANG= systemctl list-units --full --no-legend`;
+    # Use current version of systemctl binary before daemon is reexeced.
+    my $lines = `LANG= /run/current-system/sw/bin/systemctl list-units --full --no-legend`;
     my $res = {};
     foreach my $line (split '\n', $lines) {
         chomp $line;
@@ -262,7 +267,8 @@ while (my ($unit, $state) = each %{$activePrev}) {
 
 sub pathToUnitName {
     my ($path) = @_;
-    open my $cmd, "-|", "@systemd@/bin/systemd-escape", "--suffix=mount", "-p", $path
+    # Use current version of systemctl binary before daemon is reexeced.
+    open my $cmd, "-|", "/run/current-system/sw/bin/systemd-escape", "--suffix=mount", "-p", $path
         or die "Unable to escape $path!\n";
     my $escaped = join "", <$cmd>;
     chomp $escaped;
@@ -364,7 +370,8 @@ syslog(LOG_NOTICE, "switching to system configuration $out");
 if (scalar (keys %unitsToStop) > 0) {
     print STDERR "stopping the following units: ", join(", ", @unitsToStopFiltered), "\n"
         if scalar @unitsToStopFiltered;
-    system("systemctl", "stop", "--", sort(keys %unitsToStop)); # FIXME: ignore errors?
+    # Use current version of systemctl binary before daemon is reexeced.
+    system("/run/current-system/sw/bin/systemctl", "stop", "--", sort(keys %unitsToStop)); # FIXME: ignore errors?
 }
 
 print STDERR "NOT restarting the following changed units: ", join(", ", sort(keys %unitsToSkip)), "\n"
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 67cb2264e3f3..091a2e412eed 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -26,11 +26,12 @@ let
      cloner false config.nesting.children
   ++ cloner true config.nesting.clone;
 
-
   systemBuilder =
     let
       kernelPath = "${config.boot.kernelPackages.kernel}/" +
         "${config.system.boot.loader.kernelFile}";
+      initrdPath = "${config.system.build.initialRamdisk}/" +
+        "${config.system.boot.loader.initrdFile}";
     in ''
       mkdir $out
 
@@ -51,7 +52,7 @@ let
 
         echo -n "$kernelParams" > $out/kernel-params
 
-        ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
+        ln -s ${initrdPath} $out/initrd
 
         ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
 
@@ -83,6 +84,7 @@ let
       done
 
       mkdir $out/bin
+      export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
       substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
       chmod +x $out/bin/switch-to-configuration
 
@@ -106,7 +108,7 @@ let
     if [] == failed then pkgs.stdenvNoCC.mkDerivation {
       name = let hn = config.networking.hostName;
                  nn = if (hn != "") then hn else "unnamed";
-          in "nixos-system-${nn}-${config.system.nixosLabel}";
+          in "nixos-system-${nn}-${config.system.nixos.label}";
       preferLocalBuild = true;
       allowSubstitutes = false;
       buildCommand = systemBuilder;
@@ -120,7 +122,7 @@ let
         config.system.build.installBootLoader
         or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
       activationScript = config.system.activationScripts.script;
-      nixosLabel = config.system.nixosLabel;
+      nixosLabel = config.system.nixos.label;
 
       configurationName = config.boot.loader.grub.configurationName;
 
@@ -179,6 +181,15 @@ in
       '';
     };
 
+    system.boot.loader.initrdFile = mkOption {
+      internal = true;
+      default = "initrd";
+      type = types.str;
+      description = ''
+        Name of the initrd file to be passed to the bootloader.
+      '';
+    };
+
     system.copySystemConfiguration = mkOption {
       type = types.bool;
       default = false;
diff --git a/nixos/modules/system/boot/binfmt.nix b/nixos/modules/system/boot/binfmt.nix
new file mode 100644
index 000000000000..15e84dc021e2
--- /dev/null
+++ b/nixos/modules/system/boot/binfmt.nix
@@ -0,0 +1,139 @@
+{ config, lib, ... }:
+let
+  inherit (lib) mkOption types optionalString;
+
+  cfg = config.boot.binfmtMiscRegistrations;
+
+  makeBinfmtLine = name: { recognitionType, offset, magicOrExtension
+                         , mask, preserveArgvZero, openBinary
+                         , matchCredentials, fixBinary, ...
+                         }: let
+    type = if recognitionType == "magic" then "M" else "E";
+    offset' = toString offset;
+    mask' = toString mask;
+    interpreter = "/run/binfmt/${name}";
+    flags = if !(matchCredentials -> openBinary)
+              then throw "boot.binfmtMiscRegistrations.${name}: you can't specify openBinary = false when matchCredentials = true."
+            else optionalString preserveArgvZero "P" +
+                 optionalString (openBinary && !matchCredentials) "O" +
+                 optionalString matchCredentials "C" +
+                 optionalString fixBinary "F";
+  in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}";
+
+  binfmtFile = builtins.toFile "binfmt_nixos.conf"
+    (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine cfg));
+
+  activationSnippet = name: { interpreter, ... }:
+    "ln -sf ${interpreter} /run/binfmt/${name}";
+  activationScript = ''
+    mkdir -p -m 0755 /run/binfmt
+    ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet cfg)}
+  '';
+in {
+  options = {
+    boot.binfmtMiscRegistrations = mkOption {
+      default = {};
+
+      description = ''
+        Extra binary formats to register with the kernel.
+        See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details.
+      '';
+
+      type = types.attrsOf (types.submodule ({ config, ... }: {
+        options = {
+          recognitionType = mkOption {
+            default = "magic";
+            description = "Whether to recognize executables by magic number or extension.";
+            type = types.enum [ "magic" "extension" ];
+          };
+
+          offset = mkOption {
+            default = null;
+            description = "The byte offset of the magic number used for recognition.";
+            type = types.nullOr types.int;
+          };
+
+          magicOrExtension = mkOption {
+            description = "The magic number or extension to match on.";
+            type = types.str;
+          };
+
+          mask = mkOption {
+            default = null;
+            description =
+              "A mask to be ANDed with the byte sequence of the file before matching";
+            type = types.nullOr types.str;
+          };
+
+          interpreter = mkOption {
+            description = ''
+              The interpreter to invoke to run the program.
+
+              Note that the actual registration will point to
+              /run/binfmt/''${name}, so the kernel interpreter length
+              limit doesn't apply.
+            '';
+            type = types.path;
+          };
+
+          preserveArgvZero = mkOption {
+            default = false;
+            description = ''
+              Whether to pass the original argv[0] to the interpreter.
+
+              See the description of the 'P' flag in the kernel docs
+              for more details;
+            '';
+            type = types.bool;
+          };
+
+          openBinary = mkOption {
+            default = config.matchCredentials;
+            description = ''
+              Whether to pass the binary to the interpreter as an open
+              file descriptor, instead of a path.
+            '';
+            type = types.bool;
+          };
+
+          matchCredentials = mkOption {
+            default = false;
+            description = ''
+              Whether to launch with the credentials and security
+              token of the binary, not the interpreter (e.g. setuid
+              bit).
+
+              See the description of the 'C' flag in the kernel docs
+              for more details.
+
+              Implies/requires openBinary = true.
+            '';
+            type = types.bool;
+          };
+
+          fixBinary = mkOption {
+            default = false;
+            description = ''
+              Whether to open the interpreter file as soon as the
+              registration is loaded, rather than waiting for a
+              relevant file to be invoked.
+
+              See the description of the 'F' flag in the kernel docs
+              for more details.
+            '';
+            type = types.bool;
+          };
+        };
+      }));
+    };
+  };
+
+  config = lib.mkIf (cfg != {}) {
+    environment.etc."binfmt.d/nixos.conf".source = binfmtFile;
+    system.activationScripts.binfmt = activationScript;
+    systemd.additionalUpstreamSystemUnits =
+      [ "proc-sys-fs-binfmt_misc.automount"
+        "proc-sys-fs-binfmt_misc.mount"
+      ];
+  };
+}
diff --git a/nixos/modules/system/boot/grow-partition.nix b/nixos/modules/system/boot/grow-partition.nix
new file mode 100644
index 000000000000..1e6f9e442b67
--- /dev/null
+++ b/nixos/modules/system/boot/grow-partition.nix
@@ -0,0 +1,50 @@
+# This module automatically grows the root partition.
+# This allows an instance to be created with a bigger root filesystem
+# than provided by the machine image.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  options = {
+    boot.growPartition = mkEnableOption "grow the root partition on boot";
+  };
+
+  config = mkIf config.boot.growPartition {
+
+    boot.initrd.extraUtilsCommands = ''
+      copy_bin_and_libs ${pkgs.gawk}/bin/gawk
+      copy_bin_and_libs ${pkgs.gnused}/bin/sed
+      copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
+      copy_bin_and_libs ${pkgs.utillinux}/sbin/lsblk
+
+      substitute "${pkgs.cloud-utils}/bin/.growpart-wrapped" "$out/bin/growpart" \
+        --replace "${pkgs.bash}/bin/sh" "/bin/sh" \
+        --replace "awk" "gawk" \
+        --replace "sed" "gnused"
+
+      ln -s sed $out/bin/gnused
+    '';
+
+    boot.initrd.postDeviceCommands = ''
+      rootDevice="${config.fileSystems."/".device}"
+      if [ -e "$rootDevice" ]; then
+        rootDevice="$(readlink -f "$rootDevice")"
+        parentDevice="$rootDevice"
+        while [ "''${parentDevice%[0-9]}" != "''${parentDevice}" ]; do
+          parentDevice="''${parentDevice%[0-9]}";
+        done
+        partNum="''${rootDevice#''${parentDevice}}"
+        if [ "''${parentDevice%[0-9]p}" != "''${parentDevice}" ] && [ -b "''${parentDevice%p}" ]; then
+          parentDevice="''${parentDevice%p}"
+        fi
+        TMPDIR=/run sh $(type -P growpart) "$parentDevice" "$partNum"
+        udevadm settle
+      fi
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix
index 6e226c190609..33862b0965cc 100644
--- a/nixos/modules/system/boot/initrd-network.nix
+++ b/nixos/modules/system/boot/initrd-network.nix
@@ -23,6 +23,8 @@ let
       fi
     '';
 
+  udhcpcArgs = toString cfg.udhcpc.extraArgs;
+
 in
 
 {
@@ -40,6 +42,20 @@ in
         kernel documentation</link>.  Otherwise, if
         <option>networking.useDHCP</option> is enabled, an IP address
         is acquired using DHCP.
+
+        You should add the module(s) required for your network card to
+        boot.initrd.availableKernelModules. lspci -v -s &lt;ethernet controller&gt;
+        will tell you which.
+      '';
+    };
+
+    boot.initrd.network.udhcpc.extraArgs = mkOption {
+      default = [];
+      type = types.listOf types.str;
+      description = ''
+        Additional command-line arguments passed verbatim to udhcpc if
+        <option>boot.initrd.network.enable</option> and <option>networking.useDHCP</option>
+        are enabled.
       '';
     };
 
@@ -87,7 +103,7 @@ in
 
           # Acquire a DHCP lease.
           echo "acquiring IP address via DHCP..."
-          udhcpc --quit --now --script ${udhcpcScript} && hasNetwork=1
+          udhcpc --quit --now --script ${udhcpcScript} ${udhcpcArgs} && hasNetwork=1
         fi
       ''
 
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index d78775c27582..8b3dc2d90eb3 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -89,9 +89,6 @@ in
 
   config = mkIf (config.boot.initrd.network.enable && cfg.enable) {
     assertions = [
-      { assertion = cfg.hostRSAKey != null || cfg.hostDSSKey != null || cfg.hostECDSAKey != null;
-        message = "You should specify at least one host key for initrd SSH";
-      }
       { assertion = cfg.authorizedKeys != [];
         message = "You should specify at least one authorized key for initrd SSH";
       }
@@ -121,7 +118,7 @@ in
         echo ${escapeShellArg key} >> /root/.ssh/authorized_keys
       '') cfg.authorizedKeys)}
 
-      dropbear -s -j -k -E -m -p ${toString cfg.port}
+      dropbear -s -j -k -E -p ${toString cfg.port} ${optionalString (cfg.hostRSAKey == null && cfg.hostDSSKey == null && cfg.hostECDSAKey == null) "-R"}
     '';
 
     boot.initrd.secrets =
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 4db9631743e3..8ea05ed14687 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -5,7 +5,7 @@ with lib;
 let
 
   inherit (config.boot) kernelPatches;
-
+  inherit (config.boot.kernel) features;
   inherit (config.boot.kernelPackages) kernel;
 
   kernelModulesConf = pkgs.writeText "nixos.conf"
@@ -21,11 +21,25 @@ in
 
   options = {
 
+    boot.kernel.features = mkOption {
+      default = {};
+      example = literalExample "{ debug = true; }";
+      internal = true;
+      description = ''
+        This option allows to enable or disable certain kernel features.
+        It's not API, because it's about kernel feature sets, that
+        make sense for specific use cases. Mostly along with programs,
+        which would have separate nixos options.
+        `grep features pkgs/os-specific/linux/kernel/common-config.nix`
+      '';
+    };
+
     boot.kernelPackages = mkOption {
       default = pkgs.linuxPackages;
       apply = kernelPackages: kernelPackages.extend (self: super: {
         kernel = super.kernel.override {
           kernelPatches = super.kernel.kernelPatches ++ kernelPatches;
+          features = lib.recursiveUpdate super.kernel.features features;
         };
       });
       # We don't want to evaluate all of linuxPackages for the manual
@@ -63,8 +77,8 @@ in
       type = types.int;
       default = 4;
       description = ''
-        The kernel console log level.  Log messages with a priority
-        numerically less than this will not appear on the console.
+        The kernel console <literal>loglevel</literal>. All Kernel Messages with a log level smaller
+        than this setting will be printed to the console.
       '';
     };
 
@@ -170,7 +184,7 @@ in
       [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
       optionals config.boot.vesa [ "vga=0x317" ];
 
-    boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
+    boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel;
 
     boot.kernelModules = [ "loop" "atkbd" ];
 
@@ -197,7 +211,7 @@ in
         "mmc_block"
 
         # Support USB keyboards, in case the boot fails and we only have
-        # a USB keyboard.
+        # a USB keyboard, or for LUKS passphrase prompt.
         "uhci_hcd"
         "ehci_hcd"
         "ehci_pci"
@@ -207,11 +221,13 @@ in
         "xhci_pci"
         "usbhid"
         "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
+        "hid_logitech_hidpp" "hid_logitech_dj"
 
-        # Misc. keyboard stuff.
+      ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
+        # Misc. x86 keyboard stuff.
         "pcips2" "atkbd" "i8042"
 
-        # Needed by the stage 2 init script.
+        # x86 RTC needed by the stage 2 init script.
         "rtc_cmos"
       ];
 
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
index b7821f9509f1..3fc1af28f628 100644
--- a/nixos/modules/system/boot/kexec.nix
+++ b/nixos/modules/system/boot/kexec.nix
@@ -1,21 +1,22 @@
-{ config, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 {
-  environment.systemPackages = [ pkgs.kexectools ];
+  config = lib.mkIf (pkgs.kexectools.meta.available) {
+    environment.systemPackages = [ pkgs.kexectools ];
 
-  systemd.services."prepare-kexec" =
-    { description = "Preparation for kexec";
-      wantedBy = [ "kexec.target" ];
-      before = [ "systemd-kexec.service" ];
-      unitConfig.DefaultDependencies = false;
-      serviceConfig.Type = "oneshot";
-      path = [ pkgs.kexectools ];
-      script =
-        ''
-          p=$(readlink -f /nix/var/nix/profiles/system)
-          if ! [ -d $p ]; then exit 1; fi
-          exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
-        '';
-    };
-
-}
\ No newline at end of file
+    systemd.services."prepare-kexec" =
+      { description = "Preparation for kexec";
+        wantedBy = [ "kexec.target" ];
+        before = [ "systemd-kexec.service" ];
+        unitConfig.DefaultDependencies = false;
+        serviceConfig.Type = "oneshot";
+        path = [ pkgs.kexectools ];
+        script =
+          ''
+            p=$(readlink -f /nix/var/nix/profiles/system)
+            if ! [ -d $p ]; then exit 1; fi
+            exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
+          '';
+      };
+  };
+}
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 9056121fa7d1..e2cff1c1bd94 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -40,7 +40,7 @@ let
     { splashImage = f cfg.splashImage;
       grub = f grub;
       grubTarget = f (grub.grubTarget or "");
-      shell = "${pkgs.stdenv.shell}";
+      shell = "${pkgs.runtimeShell}";
       fullName = (builtins.parseDrvName realGrub.name).name;
       fullVersion = (builtins.parseDrvName realGrub.name).version;
       grubEfi = f grubEfi;
@@ -110,7 +110,7 @@ in
 
       device = mkOption {
         default = "";
-        example = "/dev/hda";
+        example = "/dev/disk/by-id/wwn-0x500001234567890a";
         type = types.str;
         description = ''
           The device on which the GRUB boot loader will be installed.
@@ -123,7 +123,7 @@ in
 
       devices = mkOption {
         default = [];
-        example = [ "/dev/hda" ];
+        example = [ "/dev/disk/by-id/wwn-0x500001234567890a" ];
         type = types.listOf types.str;
         description = ''
           The devices on which the boot loader, GRUB, will be
@@ -135,8 +135,8 @@ in
       mirroredBoots = mkOption {
         default = [ ];
         example = [
-          { path = "/boot1"; devices = [ "/dev/sda" ]; }
-          { path = "/boot2"; devices = [ "/dev/sdb" ]; }
+          { path = "/boot1"; devices = [ "/dev/disk/by-id/wwn-0x500001234567890a" ]; }
+          { path = "/boot2"; devices = [ "/dev/disk/by-id/wwn-0x500009876543210a" ]; }
         ];
         description = ''
           Mirror the boot configuration to multiple partitions and install grub
@@ -178,7 +178,7 @@ in
 
             devices = mkOption {
               default = [ ];
-              example = [ "/dev/sda" "/dev/sdb" ];
+              example = [ "/dev/disk/by-id/wwn-0x500001234567890a" "/dev/disk/by-id/wwn-0x500009876543210a" ];
               type = types.listOf types.str;
               description = ''
                 The path to the devices which will have the GRUB MBR written.
@@ -536,9 +536,9 @@ in
             btrfsprogs = pkgs.btrfs-progs;
           };
         in pkgs.writeScript "install-grub.sh" (''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         set -e
-        export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])}
+        export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX XMLSAXBase ListCompare ])}
         ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"}
       '' + flip concatMapStrings cfg.mirroredBoots (args: ''
         ${pkgs.perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index cc03e54ead63..8bd203106f55 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -182,7 +182,7 @@ sub GrubFs {
                 # Based on the type pull in the identifier from the system
                 my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}");
                 if ($status != 0) {
-                    die "Failed to get blkid info for @{[$fs->mount]} on @{[$fs->device]}";
+                    die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}";
                 }
                 my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
                 if ($#matches != 0) {
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 06f004fb06ec..54dfb53fd30f 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -5,7 +5,7 @@ with lib;
 let
   luks = config.boot.initrd.luks;
 
-  openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, ... }: assert name' == name; ''
+  openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, fallbackToPassword, ... }: assert name' == name; ''
 
     # Wait for a target (e.g. device, keyFile, header, ...) to appear.
     wait_target() {
@@ -43,8 +43,17 @@ let
     open_normally() {
         echo luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
           ${optionalString (header != null) "--header=${header}"} \
-          ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"} \
           > /.luksopen_args
+        ${optionalString (keyFile != null) ''
+        ${optionalString fallbackToPassword "if [ -e ${keyFile} ]; then"}
+            echo " --key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}" \
+              >> /.luksopen_args
+        ${optionalString fallbackToPassword ''
+        else
+            echo "keyfile ${keyFile} not found -- fallback to interactive unlocking"
+        fi
+        ''}
+        ''}
         cryptsetup-askpass
         rm /.luksopen_args
     }
@@ -227,6 +236,7 @@ in
       default =
         [ "aes" "aes_generic" "blowfish" "twofish"
           "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
+
           (if pkgs.stdenv.system == "x86_64-linux" then "aes_x86_64" else "aes_i586")
         ];
       description = ''
@@ -323,6 +333,16 @@ in
             '';
           };
 
+          fallbackToPassword = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Whether to fallback to interactive passphrase prompt if the keyfile
+              cannot be found. This will prevent unattended boot should the keyfile
+              go missing.
+            '';
+          };
+
           yubikey = mkOption {
             default = null;
             description = ''
@@ -434,7 +454,12 @@ in
       ["firewire_ohci" "firewire_core" "firewire_sbp2"];
 
     # Some modules that may be needed for mounting anything ciphered
-    boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" ] ++ luks.cryptoModules;
+    # Also load input_leds to get caps lock light working (#12456)
+    boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" "input_leds" ]
+      ++ luks.cryptoModules
+      # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged
+      # remove once 'modprobe --show-depends xts' shows ecb as a dependency
+      ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []);
 
     # copy the cryptsetup binary and it's dependencies
     boot.initrd.extraUtilsCommands = ''
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
index b915a98d5375..dee0ab470c99 100644
--- a/nixos/modules/system/boot/modprobe.nix
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -54,7 +54,7 @@ with lib;
 
     environment.systemPackages = [ pkgs.kmod ];
 
-    system.activationScripts.modprobe =
+    system.activationScripts.modprobe = stringAfter ["specialfs"]
       ''
         # Allow the kernel to find our wrapped modprobe (which searches
         # in the right location in the Nix store for kernel modules).
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 9d2cea3ad165..eea10613ea58 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -94,7 +94,7 @@ let
   checkNetwork = checkUnitConfig "Network" [
     (assertOnlyFields [
       "Description" "DHCP" "DHCPServer" "IPForward" "IPMasquerade" "IPv4LL" "IPv4LLRoute"
-      "LLMNR" "MulticastDNS" "Domains" "Bridge" "Bond"
+      "LLMNR" "MulticastDNS" "Domains" "Bridge" "Bond" "IPv6PrivacyExtensions"
     ])
     (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"])
     (assertValueOneOf "DHCPServer" boolValues)
@@ -104,6 +104,7 @@ let
     (assertValueOneOf "IPv4LLRoute" boolValues)
     (assertValueOneOf "LLMNR" boolValues)
     (assertValueOneOf "MulticastDNS" boolValues)
+    (assertValueOneOf "IPv6PrivacyExtensions" ["yes" "no" "prefer-public" "kernel"])
   ];
 
   checkAddress = checkUnitConfig "Address" [
@@ -700,7 +701,6 @@ in
 
     systemd.additionalUpstreamSystemUnits = [
       "systemd-networkd.service" "systemd-networkd-wait-online.service"
-      "org.freedesktop.network1.busname"
     ];
 
     systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links
diff --git a/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix
index 4b0c498424b5..f8fb8a64cb9b 100644
--- a/nixos/modules/system/boot/plymouth.nix
+++ b/nixos/modules/system/boot/plymouth.nix
@@ -8,9 +8,14 @@ let
 
   cfg = config.boot.plymouth;
 
+  breezePlymouth = pkgs.breeze-plymouth.override {
+    nixosBranding = true;
+    nixosVersion = config.system.nixos.release;
+  };
+
   themesEnv = pkgs.buildEnv {
     name = "plymouth-themes";
-    paths = [ plymouth ] ++ cfg.themePackages;
+    paths = [ plymouth breezePlymouth ] ++ cfg.themePackages;
   };
 
   configFile = pkgs.writeText "plymouthd.conf" ''
@@ -38,7 +43,7 @@ in
       };
 
       theme = mkOption {
-        default = "fade-in";
+        default = "breeze";
         type = types.str;
         description = ''
           Splash screen theme.
diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix
index 2147d43c4f19..4d9de020c84e 100644
--- a/nixos/modules/system/boot/resolved.nix
+++ b/nixos/modules/system/boot/resolved.nix
@@ -126,7 +126,7 @@ in
   config = mkIf cfg.enable {
 
     systemd.additionalUpstreamSystemUnits = [
-      "systemd-resolved.service" "org.freedesktop.resolve1.busname"
+      "systemd-resolved.service"
     ];
 
     systemd.services.systemd-resolved = {
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index b442386914ad..964ec68cfe2f 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -167,6 +167,7 @@ done
 # Load the required kernel modules.
 mkdir -p /lib
 ln -s @modulesClosure@/lib/modules /lib/modules
+ln -s @modulesClosure@/lib/firmware /lib/firmware
 echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
 for i in @kernelModules@; do
     echo "loading module $(basename $i)..."
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index d6e3e3a87d01..55bb6d3449c5 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -13,12 +13,14 @@ let
 
   kernelPackages = config.boot.kernelPackages;
   modulesTree = config.system.modulesTree;
+  firmware = config.hardware.firmware;
 
 
   # Determine the set of modules that we need to mount the root FS.
   modulesClosure = pkgs.makeModulesClosure {
     rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
     kernel = modulesTree;
+    firmware = firmware;
     allowMissing = true;
   };
 
@@ -28,6 +30,50 @@ let
   # mounting `/`, like `/` on a loopback).
   fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
 
+  # A utility for enumerating the shared-library dependencies of a program
+  findLibs = pkgs.writeShellScriptBin "find-libs" ''
+    set -euo pipefail
+
+    declare -A seen
+    declare -a left
+
+    patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
+
+    function add_needed {
+      rpath="$($patchelf --print-rpath $1)"
+      dir="$(dirname $1)"
+      for lib in $($patchelf --print-needed $1); do
+        left+=("$lib" "$rpath" "$dir")
+      done
+    }
+
+    add_needed $1
+
+    while [ ''${#left[@]} -ne 0 ]; do
+      next=''${left[0]}
+      rpath=''${left[1]}
+      ORIGIN=''${left[2]}
+      left=("''${left[@]:3}")
+      if [ -z ''${seen[$next]+x} ]; then
+        seen[$next]=1
+        IFS=: read -ra paths <<< $rpath
+        res=
+        for path in "''${paths[@]}"; do
+          path=$(eval "echo $path")
+          if [ -f "$path/$next" ]; then
+              res="$path/$next"
+              echo "$res"
+              add_needed "$res"
+              break
+          fi
+        done
+        if [ -z "$res" ]; then
+          echo "Couldn't satisfy dependency $next" >&2
+          exit 1
+        fi
+      fi
+    done
+  '';
 
   # Some additional utilities needed in stage 1, like mount, lvm, fsck
   # etc.  We don't want to bring in all of those packages, so we just
@@ -35,7 +81,7 @@ let
   # we just copy what we need from Glibc and use patchelf to make it
   # work.
   extraUtils = pkgs.runCommandCC "extra-utils"
-    { buildInputs = [pkgs.nukeReferences];
+    { nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
       allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
     }
     ''
@@ -101,9 +147,7 @@ let
       # 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
+        for LIB in $(${findLibs}/bin/find-libs $BIN); do
           TGT="$out/lib/$(basename $LIB)"
           if [ ! -f "$TGT" ]; then
             SRC="$(readlink -e $LIB)"
@@ -130,6 +174,7 @@ let
         fi
       done
 
+      if [ -z "${toString pkgs.stdenv.isCross}" ]; then
       # Make sure that the patchelf'ed binaries still work.
       echo "testing patched programs..."
       $out/bin/ash -c 'echo hello world' | grep "hello world"
@@ -142,6 +187,7 @@ let
       $out/bin/mdadm --version
 
       ${config.boot.initrd.extraUtilsCommandsTest}
+      fi
     ''; # */
 
 
@@ -243,7 +289,7 @@ let
             { src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf"; }
             ''
               target=$out
-              ${pkgs.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
+              ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
             '';
           symlink = "/etc/modprobe.d/ubuntu.conf";
         }
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index 46aed44bf10f..b83012dfda7e 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -43,7 +43,7 @@ if [ ! -e /proc/1 ]; then
         local options="$3"
         local fsType="$4"
 
-        mkdir -m 0755 -p "$mountPoint"
+        install -m 0755 -d "$mountPoint"
         mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
     }
     source @earlyMountScript@
@@ -71,7 +71,7 @@ fi
 
 
 # Provide a /etc/mtab.
-mkdir -m 0755 -p /etc
+install -m 0755 -d /etc
 test -e /etc/fstab || touch /etc/fstab # to shut up mount
 rm -f /etc/mtab* # not that we care about stale locks
 ln -s /proc/mounts /etc/mtab
@@ -79,10 +79,9 @@ ln -s /proc/mounts /etc/mtab
 
 # More special file systems, initialise required directories.
 [ -e /proc/bus/usb ] && mount -t usbfs usbfs /proc/bus/usb # UML doesn't have USB by default
-mkdir -m 01777 -p /tmp
-mkdir -m 0755 -p /var/{log,lib,db} /nix/var /etc/nixos/ \
+install -m 01777 -d /tmp
+install -m 0755 -d /var/{log,lib,db} /nix/var /etc/nixos/ \
     /run/lock /home /bin # for the /bin/sh symlink
-install -m 0700 -d /root
 
 
 # Miscellaneous boot time cleanup.
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index 8db6d2d2f734..78afbd8dbc12 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -10,6 +10,7 @@ let
   bootStage2 = pkgs.substituteAll {
     src = ./stage-2-init.sh;
     shellDebug = "${pkgs.bashInteractive}/bin/bash";
+    shell = "${pkgs.bash}/bin/bash";
     isExecutable = true;
     inherit (config.nix) readOnlyStore;
     inherit (config.networking) useHostResolvConf;
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index 43a9c28bb694..5255f1a1b97a 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -217,7 +217,7 @@ in rec {
 
     environment = mkOption {
       default = {};
-      type = types.attrs; # FIXME
+      type = with types; attrsOf (nullOr (either str package));
       example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
       description = "Environment variables passed to the service's processes.";
     };
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index dd9ba7104485..d2fe33488a7a 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -14,7 +14,6 @@ let
   upstreamSystemUnits =
     [ # Targets.
       "basic.target"
-      "busnames.target"
       "sysinit.target"
       "sockets.target"
       "exit.target"
@@ -47,6 +46,7 @@ let
 
       # Consoles.
       "getty.target"
+      "getty-pre.target"
       "getty@.service"
       "serial-getty@.service"
       "console-getty.service"
@@ -63,10 +63,7 @@ let
       "systemd-logind.service"
       "autovt@.service"
       "systemd-user-sessions.service"
-      "dbus-org.freedesktop.login1.service"
       "dbus-org.freedesktop.machine1.service"
-      "org.freedesktop.login1.busname"
-      "org.freedesktop.machine1.busname"
       "user@.service"
 
       # Journal.
@@ -99,7 +96,6 @@ let
       "swap.target"
       "dev-hugepages.mount"
       "dev-mqueue.mount"
-      "proc-sys-fs-binfmt_misc.mount"
       "sys-fs-fuse-connections.mount"
       "sys-kernel-config.mount"
       "sys-kernel-debug.mount"
@@ -141,7 +137,6 @@ let
 
       # Slices / containers.
       "slices.target"
-      "system.slice"
       "user.slice"
       "machine.slice"
       "machines.target"
@@ -155,19 +150,16 @@ let
       "systemd-tmpfiles-setup-dev.service"
 
       # Misc.
-      "org.freedesktop.systemd1.busname"
       "systemd-sysctl.service"
       "dbus-org.freedesktop.timedate1.service"
       "dbus-org.freedesktop.locale1.service"
       "dbus-org.freedesktop.hostname1.service"
-      "org.freedesktop.timedate1.busname"
-      "org.freedesktop.locale1.busname"
-      "org.freedesktop.hostname1.busname"
       "systemd-timedated.service"
       "systemd-localed.service"
       "systemd-hostnamed.service"
       "systemd-binfmt.service"
       "systemd-exit.service"
+      "systemd-update-done.service"
     ]
     ++ cfg.additionalUpstreamSystemUnits;
 
@@ -182,7 +174,6 @@ let
   upstreamUserUnits =
     [ "basic.target"
       "bluetooth.target"
-      "busnames.target"
       "default.target"
       "exit.target"
       "graphical-session-pre.target"
@@ -249,37 +240,37 @@ let
         }
         (mkIf (config.preStart != "")
           { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.preStart}
             '';
           })
         (mkIf (config.script != "")
           { serviceConfig.ExecStart = makeJobScript "${name}-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.script}
             '' + " " + config.scriptArgs;
           })
         (mkIf (config.postStart != "")
           { serviceConfig.ExecStartPost = makeJobScript "${name}-post-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.postStart}
             '';
           })
         (mkIf (config.reload != "")
           { serviceConfig.ExecReload = makeJobScript "${name}-reload" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.reload}
             '';
           })
         (mkIf (config.preStop != "")
           { serviceConfig.ExecStop = makeJobScript "${name}-pre-stop" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.preStop}
             '';
           })
         (mkIf (config.postStop != "")
           { serviceConfig.ExecStopPost = makeJobScript "${name}-post-stop" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.postStop}
             '';
           })
@@ -524,7 +515,7 @@ in
     };
 
     systemd.globalEnvironment = mkOption {
-      type = types.attrs;
+      type = with types; attrsOf (nullOr (either str package));
       default = {};
       example = { TZ = "CET"; };
       description = ''
@@ -532,6 +523,14 @@ in
       '';
     };
 
+    systemd.enableCgroupAccounting = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable cgroup accounting.
+      '';
+    };
+
     systemd.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -733,6 +732,13 @@ in
 
       "systemd/system.conf".text = ''
         [Manager]
+        ${optionalString config.systemd.enableCgroupAccounting ''
+          DefaultCPUAccounting=yes
+          DefaultIOAccounting=yes
+          DefaultBlockIOAccounting=yes
+          DefaultMemoryAccounting=yes
+          DefaultTasksAccounting=yes
+        ''}
         ${config.systemd.extraConfig}
       '';
 
@@ -789,8 +795,7 @@ in
 
         # Keep a persistent journal. Note that systemd-tmpfiles will
         # set proper ownership/permissions.
-        # FIXME: revert to 0700 with systemd v233.
-        mkdir -m 0750 -p /var/log/journal
+        mkdir -m 0700 -p /var/log/journal
       '';
 
     users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network;
@@ -830,7 +835,8 @@ in
 
     system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
       [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
-        "SYSFS" "PROC_FS" "FHANDLE" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL"
+        "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"
+        "CRYPTO_SHA256" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL"
         "TMPFS_XATTR" "SECCOMP"
       ];
 
@@ -887,7 +893,7 @@ in
     systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
-    systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.automount" ];
+    systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.mount" ];
 
     # Don't bother with certain units in containers.
     systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
diff --git a/nixos/modules/tasks/filesystems/btrfs.nix b/nixos/modules/tasks/filesystems/btrfs.nix
index 8cfa1b6921d3..1384873b6631 100644
--- a/nixos/modules/tasks/filesystems/btrfs.nix
+++ b/nixos/modules/tasks/filesystems/btrfs.nix
@@ -1,35 +1,132 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, utils, ... }:
 
 with lib;
 
 let
 
   inInitrd = any (fs: fs == "btrfs") config.boot.initrd.supportedFilesystems;
+  inSystem = any (fs: fs == "btrfs") config.boot.supportedFilesystems;
+
+  cfgScrub = config.services.btrfs.autoScrub;
+
+  enableAutoScrub = cfgScrub.enable;
+  enableBtrfs = inInitrd || inSystem || enableAutoScrub;
 
 in
 
 {
-  config = mkIf (any (fs: fs == "btrfs") config.boot.supportedFilesystems) {
+  options = {
+    # One could also do regular btrfs balances, but that shouldn't be necessary
+    # during normal usage and as long as the filesystems aren't filled near capacity
+    services.btrfs.autoScrub = {
+      enable = mkEnableOption "Enable regular btrfs scrub";
 
-    system.fsPackages = [ pkgs.btrfs-progs ];
+      fileSystems = mkOption {
+        type = types.listOf types.path;
+        example = [ "/" ];
+        description = ''
+          List of paths to btrfs filesystems to regularily call <command>btrfs scrub</command> on.
+          Defaults to all mount points with btrfs filesystems.
+          If you mount a filesystem multiple times or additionally mount subvolumes,
+          you need to manually specify this list to avoid scrubbing multiple times.
+        '';
+      };
 
-    boot.initrd.kernelModules = mkIf inInitrd [ "btrfs" "crc32c" ];
+      interval = mkOption {
+        default = "monthly";
+        type = types.str;
+        example = "weekly";
+        description = ''
+          Systemd calendar expression for when to scrub btrfs filesystems.
+          The recommended period is a month but could be less
+          (<citerefentry><refentrytitle>btrfs-scrub</refentrytitle>
+          <manvolnum>8</manvolnum></citerefentry>).
+          See
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>7</manvolnum></citerefentry>
+          for more information on the syntax.
+        '';
+      };
+
+    };
+  };
 
-    boot.initrd.extraUtilsCommands = mkIf inInitrd
+  config = mkMerge [
+    (mkIf enableBtrfs {
+      system.fsPackages = [ pkgs.btrfs-progs ];
+
+      boot.initrd.kernelModules = mkIf inInitrd [ "btrfs" "crc32c" ];
+
+      boot.initrd.extraUtilsCommands = mkIf inInitrd
       ''
         copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfs
         ln -sv btrfs $out/bin/btrfsck
         ln -sv btrfsck $out/bin/fsck.btrfs
       '';
 
-    boot.initrd.extraUtilsCommandsTest = mkIf inInitrd
+      boot.initrd.extraUtilsCommandsTest = mkIf inInitrd
       ''
         $out/bin/btrfs --version
       '';
 
-    boot.initrd.postDeviceCommands = mkIf inInitrd
+      boot.initrd.postDeviceCommands = mkIf inInitrd
       ''
         btrfs device scan
       '';
-  };
+    })
+
+    (mkIf enableAutoScrub {
+      assertions = [
+        {
+          assertion = cfgScrub.enable -> (cfgScrub.fileSystems != []);
+          message = ''
+            If 'services.btrfs.autoScrub' is enabled, you need to have at least one
+            btrfs file system mounted via 'fileSystems' or specify a list manually
+            in 'services.btrfs.autoScrub.fileSystems'.
+          '';
+        }
+      ];
+
+      # This will yield duplicated units if the user mounts a filesystem multiple times
+      # or additionally mounts subvolumes, but going the other way around via devices would
+      # yield duplicated units when a filesystem spans multiple devices.
+      # This way around seems like the more sensible default.
+      services.btrfs.autoScrub.fileSystems = mkDefault (mapAttrsToList (name: fs: fs.mountPoint)
+      (filterAttrs (name: fs: fs.fsType == "btrfs") config.fileSystems));
+
+      # TODO: Did not manage to do it via the usual btrfs-scrub@.timer/.service
+      # template units due to problems enabling the parameterized units,
+      # so settled with many units and templating via nix for now.
+      # https://github.com/NixOS/nixpkgs/pull/32496#discussion_r156527544
+      systemd.timers = let
+        scrubTimer = fs: let
+          fs' = utils.escapeSystemdPath fs;
+        in nameValuePair "btrfs-scrub-${fs'}" {
+          description = "regular btrfs scrub timer on ${fs}";
+
+          wantedBy = [ "timers.target" ];
+          timerConfig = {
+            OnCalendar = cfgScrub.interval;
+            AccuracySec = "1d";
+            Persistent = true;
+          };
+        };
+      in listToAttrs (map scrubTimer cfgScrub.fileSystems);
+
+      systemd.services = let
+        scrubService = fs: let
+          fs' = utils.escapeSystemdPath fs;
+        in nameValuePair "btrfs-scrub-${fs'}" {
+          description = "btrfs scrub on ${fs}";
+
+          serviceConfig = {
+            Type = "oneshot";
+            Nice = 19;
+            IOSchedulingClass = "idle";
+            ExecStart = "${pkgs.btrfs-progs}/bin/btrfs scrub start -B ${fs}";
+          };
+        };
+      in listToAttrs (map scrubService cfgScrub.fileSystems);
+    })
+  ];
 }
diff --git a/nixos/modules/tasks/filesystems/exfat.nix b/nixos/modules/tasks/filesystems/exfat.nix
index 963bc940b4fa..1527f993fdd4 100644
--- a/nixos/modules/tasks/filesystems/exfat.nix
+++ b/nixos/modules/tasks/filesystems/exfat.nix
@@ -5,7 +5,7 @@ with lib;
 {
   config = mkIf (any (fs: fs == "exfat") config.boot.supportedFilesystems) {
 
-    system.fsPackages = [ pkgs.exfat-utils pkgs.fuse_exfat ];
+    system.fsPackages = [ pkgs.exfat ];
 
   };
 }
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 7fee99115329..c3bf897d51fd 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -24,7 +24,11 @@ let
 
   kernel = config.boot.kernelPackages;
 
-  packages = if config.boot.zfs.enableUnstable then {
+  packages = if config.boot.zfs.enableLegacyCrypto then {
+    spl = kernel.splLegacyCrypto;
+    zfs = kernel.zfsLegacyCrypto;
+    zfsUser = pkgs.zfsLegacyCrypto;
+  } else if config.boot.zfs.enableUnstable then {
     spl = kernel.splUnstable;
     zfs = kernel.zfsUnstable;
     zfsUser = pkgs.zfsUnstable;
@@ -75,6 +79,27 @@ in
           '';
       };
 
+      enableLegacyCrypto = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enabling this option will allow you to continue to use the old format for
+          encrypted datasets. With the inclusion of stability patches the format of
+          encrypted datasets has changed. They can still be accessed and mounted but
+          in read-only mode mounted. It is highly recommended to convert them to
+          the new format.
+
+          This option is only for convenience to people that cannot convert their
+          datasets to the new format yet and it will be removed in due time.
+
+          For migration strategies from old format to this new one, check the Wiki:
+          https://nixos.wiki/wiki/NixOS_on_ZFS#Encrypted_Dataset_Format_Change
+
+          See https://github.com/zfsonlinux/zfs/pull/6864 for more details about
+          the stability patches.
+          '';
+      };
+
       extraPools = mkOption {
         type = types.listOf types.str;
         default = [];
@@ -268,7 +293,7 @@ in
       assertions = [
         {
           assertion = config.networking.hostId != null;
-          message = "ZFS requires config.networking.hostId to be set";
+          message = "ZFS requires networking.hostId to be set";
         }
         {
           assertion = !cfgZfs.forceImportAll || cfgZfs.forceImportRoot;
@@ -280,6 +305,8 @@ in
         }
       ];
 
+      virtualisation.lxd.zfsSupport = true;
+
       boot = {
         kernelModules = [ "spl" "zfs" ] ;
         extraModulePackages = with packages; [ spl zfs ];
@@ -427,7 +454,7 @@ in
                               }) snapshotNames);
 
       systemd.timers = let
-                         timer = name: if name == "frequent" then "*:15,30,45" else name;
+                         timer = name: if name == "frequent" then "*:0,15,30,45" else name;
                        in builtins.listToAttrs (map (snapName:
                             {
                               name = "zfs-snapshot-${snapName}";
diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix
index 7fb3cbc5c1bc..fbe42b8e8f04 100644
--- a/nixos/modules/tasks/kbd.nix
+++ b/nixos/modules/tasks/kbd.nix
@@ -13,7 +13,7 @@ let
   isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale);
 
   optimizedKeymap = pkgs.runCommand "keymap" {
-    nativeBuildInputs = [ pkgs.kbd ];
+    nativeBuildInputs = [ pkgs.buildPackages.kbd ];
     LOADKEYS_KEYMAP_PATH = "${kbdEnv}/share/keymaps/**";
   } ''
     loadkeys -b ${optionalString isUnicode "-u"} "${config.i18n.consoleKeyMap}" > $out
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 1f424f84c6e0..e754a1e8718d 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -20,14 +20,8 @@ let
     "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
 
   interfaceIps = i:
-    i.ip4 ++ optionals cfg.enableIPv6 i.ip6
-    ++ optional (i.ipAddress != null) {
-      address = i.ipAddress;
-      prefixLength = i.prefixLength;
-    } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
-      address = i.ipv6Address;
-      prefixLength = i.ipv6PrefixLength;
-    };
+    i.ipv4.addresses
+    ++ optionals cfg.enableIPv6 i.ipv6.addresses;
 
   destroyBond = i: ''
     while true; do
@@ -74,13 +68,12 @@ let
              (hasAttr dev cfg.macvlans) ||
              (hasAttr dev cfg.sits) ||
              (hasAttr dev cfg.vlans) ||
-             (hasAttr dev cfg.vswitches) ||
-             (hasAttr dev cfg.wlanInterfaces)
+             (hasAttr dev cfg.vswitches)
           then [ "${dev}-netdev.service" ]
           else optional (dev != null && dev != "lo" && !config.boot.isContainer) (subsystemDevice dev);
 
         hasDefaultGatewaySet = (cfg.defaultGateway != null && cfg.defaultGateway.address != "")
-                            || (cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "");
+                            || (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "");
 
         networkLocalCommands = {
           after = [ "network-setup.service" ];
@@ -185,33 +178,58 @@ let
             path = [ pkgs.iproute ];
             script =
               ''
-                # FIXME: shouldn't this be done in network-link?
-                echo "bringing up interface..."
-                ip link set "${i.name}" up
-
                 state="/run/nixos/network/addresses/${i.name}"
+                mkdir -p $(dirname "$state")
 
+                ${flip concatMapStrings ips (ip:
+                  let
+                    cidr = "${ip.address}/${toString ip.prefixLength}";
+                  in
+                  ''
+                    echo "${cidr}" >> $state
+                    echo -n "adding address ${cidr}... "
+                    if out=$(ip addr add "${cidr}" dev "${i.name}" 2>&1); then
+                      echo "done"
+                    elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
+                      echo "'ip addr add "${cidr}" dev "${i.name}"' failed: $out"
+                      exit 1
+                    fi
+                  ''
+                )}
+
+                state="/run/nixos/network/routes/${i.name}"
                 mkdir -p $(dirname "$state")
 
-              '' + flip concatMapStrings (ips) (ip:
-                let
-                  address = "${ip.address}/${toString ip.prefixLength}";
-                in
-                ''
-                  echo "${address}" >> $state
-                  if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
-                    echo "added ip ${address}"
-                  elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
-                    echo "failed to add ${address}"
-                    exit 1
-                  fi
-                '');
+                ${flip concatMapStrings (i.ipv4.routes ++ i.ipv6.routes) (route:
+                  let
+                    cidr = "${route.address}/${toString route.prefixLength}";
+                    via = optionalString (route.via != null) ''via "${route.via}"'';
+                    options = concatStrings (mapAttrsToList (name: val: "${name} ${val} ") route.options);
+                  in
+                  ''
+                     echo "${cidr}" >> $state
+                     echo -n "adding route ${cidr}... "
+                     if out=$(ip route add "${cidr}" ${options} ${via} dev "${i.name}" 2>&1); then
+                       echo "done"
+                     elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
+                       echo "'ip route add "${cidr}" ${options} ${via} dev "${i.name}"' failed: $out"
+                       exit 1
+                     fi
+                  ''
+                )}
+              '';
             preStop = ''
+              state="/run/nixos/network/routes/${i.name}"
+              while read cidr; do
+                echo -n "deleting route $cidr... "
+                ip route del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed"
+              done < "$state"
+              rm -f "$state"
+
               state="/run/nixos/network/addresses/${i.name}"
-              while read address; do
-                echo -n "deleting $address..."
-                ip addr del "$address" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed"
-                echo ""
+              while read cidr; do
+                echo -n "deleting address $cidr... "
+                ip addr del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed"
               done < "$state"
               rm -f "$state"
             '';
@@ -230,9 +248,7 @@ let
               RemainAfterExit = true;
             };
             script = ''
-              ip tuntap add dev "${i.name}" \
-              ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \
-              user "${i.virtualOwner}"
+              ip tuntap add dev "${i.name}" mode "${i.virtualType}" user "${i.virtualOwner}"
             '';
             postStop = ''
               ip link del ${i.name} || true
@@ -271,6 +287,17 @@ let
                 ${i}
               '')}" > /run/${n}.interfaces
 
+              ${optionalString config.virtualisation.libvirtd.enable ''
+                  # Enslave dynamically added interfaces which may be lost on nixos-rebuild
+                  for uri in qemu:///system lxc:///; do
+                    for dom in $(${pkgs.libvirt}/bin/virsh -c $uri list --name); do
+                      ${pkgs.libvirt}/bin/virsh -c $uri dumpxml "$dom" | \
+                      ${pkgs.xmlstarlet}/bin/xmlstarlet sel -t -m "//domain/devices/interface[@type='bridge'][source/@bridge='${n}'][target/@dev]" -v "concat('ip link set ',target/@dev,' master ',source/@bridge,';')" | \
+                      ${pkgs.bash}/bin/bash
+                    done
+                  done
+                ''}
+
               # Enable stp on the interface
               ${optionalString v.rstp ''
                 echo 2 >/sys/class/net/${n}/bridge/stp_state
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index a365a01bfb1e..c640e886fca8 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -9,14 +9,8 @@ let
   interfaces = attrValues cfg.interfaces;
 
   interfaceIps = i:
-    i.ip4 ++ optionals cfg.enableIPv6 i.ip6
-    ++ optional (i.ipAddress != null) {
-      address = i.ipAddress;
-      prefixLength = i.prefixLength;
-    } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
-      address = i.ipv6Address;
-      prefixLength = i.ipv6PrefixLength;
-    };
+    i.ipv4.addresses
+    ++ optionals cfg.enableIPv6 i.ipv6.addresses;
 
   dhcpStr = useDHCP: if useDHCP == true || useDHCP == null then "both" else "none";
 
@@ -74,27 +68,24 @@ in
         networks."99-main" = genericNetwork mkDefault;
       }
       (mkMerge (flip map interfaces (i: {
-        netdevs = mkIf i.virtual (
-          let
-            devType = if i.virtualType != null then i.virtualType
-              else (if hasPrefix "tun" i.name then "tun" else "tap");
-          in {
-            "40-${i.name}" = {
-              netdevConfig = {
-                Name = i.name;
-                Kind = devType;
-              };
-              "${devType}Config" = optionalAttrs (i.virtualOwner != null) {
-                User = i.virtualOwner;
-              };
+        netdevs = mkIf i.virtual ({
+          "40-${i.name}" = {
+            netdevConfig = {
+              Name = i.name;
+              Kind = i.virtualType;
+            };
+            "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) {
+              User = i.virtualOwner;
             };
-          });
+          };
+        });
         networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) {
           name = mkDefault i.name;
           DHCP = mkForce (dhcpStr
             (if i.useDHCP != null then i.useDHCP else cfg.useDHCP && interfaceIps i == [ ]));
           address = flip map (interfaceIps i)
             (ip: "${ip.address}/${toString ip.prefixLength}");
+          networkConfig.IPv6PrivacyExtensions = "kernel";
         } ];
       })))
       (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: {
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index b7e85e402aa9..14f9b9567515 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, utils, stdenv, ... }:
+{ config, options, lib, pkgs, utils, stdenv, ... }:
 
 with lib;
 with utils;
@@ -26,7 +26,7 @@ let
     executable = true;
     destination = "/bin/bridge-stp";
     text = ''
-      #!${pkgs.stdenv.shell} -e
+      #!${pkgs.runtimeShell} -e
       export PATH="${pkgs.mstpd}/bin"
 
       BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)})
@@ -62,35 +62,6 @@ let
     then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces)
     else mapAttrsToList (n: v: v // {_iName = n;}) interfaces;
 
-  # udev script that configures a physical wlan device and adds virtual interfaces
-  wlanDeviceUdevScript = device: interfaceList: pkgs.writeScript "wlan-${device}-udev-script" ''
-    #!${pkgs.stdenv.shell}
-
-    # Change the wireless phy device to a predictable name.
-    if [ -e "/sys/class/net/${device}/phy80211/name" ]; then
-      ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/${device}/phy80211/name` set name ${device} || true
-    fi
-
-    # Crate new, virtual interfaces and configure them at the same time
-    ${flip concatMapStrings (drop 1 interfaceList) (i: ''
-    ${pkgs.iw}/bin/iw dev ${device} interface add ${i._iName} type ${i.type} \
-      ${optionalString (i.type == "mesh" && i.meshID != null) "mesh_id ${i.meshID}"} \
-      ${optionalString (i.type == "monitor" && i.flags != null) "flags ${i.flags}"} \
-      ${optionalString (i.type == "managed" && i.fourAddr != null) "4addr ${if i.fourAddr then "on" else "off"}"} \
-      ${optionalString (i.mac != null) "addr ${i.mac}"}
-    '')}
-
-    # Reconfigure and rename the default interface that already exists
-    ${flip concatMapStrings (take 1 interfaceList) (i: ''
-      ${pkgs.iw}/bin/iw dev ${device} set type ${i.type}
-      ${optionalString (i.type == "mesh" && i.meshID != null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${i.meshID}"}
-      ${optionalString (i.type == "monitor" && i.flags != null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${i.flags}"}
-      ${optionalString (i.type == "managed" && i.fourAddr != null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if i.fourAddr then "on" else "off"}"}
-      ${optionalString (i.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${i.mac}"}
-      ${optionalString (device != i._iName) "${pkgs.iproute}/bin/ip link set dev ${device} name ${i._iName}"}
-    '')}
-  '';
-
   # We must escape interfaces due to the systemd interpretation
   subsystemDevice = interface:
     "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
@@ -101,7 +72,7 @@ let
         address = mkOption {
           type = types.str;
           description = ''
-            IPv${toString v} address of the interface.  Leave empty to configure the
+            IPv${toString v} address of the interface. Leave empty to configure the
             interface using DHCP.
           '';
         };
@@ -116,6 +87,40 @@ let
       };
     };
 
+  routeOpts = v:
+  { options = {
+      address = mkOption {
+        type = types.str;
+        description = "IPv${toString v} address of the network.";
+      };
+
+      prefixLength = mkOption {
+        type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
+        description = ''
+          Subnet mask of the network, specified as the number of
+          bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
+        '';
+      };
+
+      via = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "IPv${toString v} address of the next hop.";
+      };
+
+      options = mkOption {
+        type = types.attrsOf types.str;
+        default = { };
+        example = { mtu = "1492"; window = "524288"; };
+        description = ''
+          Other route options. See the symbol <literal>OPTIONS</literal>
+          in the <literal>ip-route(8)</literal> manual page for the details.
+        '';
+      };
+
+    };
+  };
+
   gatewayCoerce = address: { inherit address; };
 
   gatewayOpts = { ... }: {
@@ -148,13 +153,22 @@ let
   interfaceOpts = { name, ... }: {
 
     options = {
-
       name = mkOption {
         example = "eth0";
         type = types.str;
         description = "Name of the interface.";
       };
 
+      preferTempAddress = mkOption {
+        type = types.bool;
+        default = cfg.enableIPv6;
+        defaultText = literalExample "config.networking.enableIPv6";
+        description = ''
+          When using SLAAC prefer a temporary (IPv6) address over the EUI-64
+          address for originating connections. This is used to reduce tracking.
+        '';
+      };
+
       useDHCP = mkOption {
         type = types.nullOr types.bool;
         default = null;
@@ -165,7 +179,7 @@ let
         '';
       };
 
-      ip4 = mkOption {
+      ipv4.addresses = mkOption {
         default = [ ];
         example = [
           { address = "10.0.0.1"; prefixLength = 16; }
@@ -177,7 +191,7 @@ let
         '';
       };
 
-      ip6 = mkOption {
+      ipv6.addresses = mkOption {
         default = [ ];
         example = [
           { address = "fdfd:b3f0:482::1"; prefixLength = 48; }
@@ -189,50 +203,27 @@ let
         '';
       };
 
-      ipAddress = mkOption {
-        default = null;
-        example = "10.0.0.1";
-        type = types.nullOr types.str;
-        description = ''
-          IP address of the interface.  Leave empty to configure the
-          interface using DHCP.
-        '';
-      };
-
-      prefixLength = mkOption {
-        default = null;
-        example = 24;
-        type = types.nullOr types.int;
-        description = ''
-          Subnet mask of the interface, specified as the number of
-          bits in the prefix (<literal>24</literal>).
-        '';
-      };
-
-      subnetMask = mkOption {
-        default = null;
-        description = ''
-          Defunct, supply the prefix length instead.
-        '';
-      };
-
-      ipv6Address = mkOption {
-        default = null;
-        example = "2001:1470:fffd:2098::e006";
-        type = types.nullOr types.str;
+      ipv4.routes = mkOption {
+        default = [];
+        example = [
+          { address = "10.0.0.0"; prefixLength = 16; }
+          { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
+        ];
+        type = with types; listOf (submodule (routeOpts 4));
         description = ''
-          IPv6 address of the interface.  Leave empty to configure the
-          interface using NDP.
+          List of extra IPv4 static routes that will be assigned to the interface.
         '';
       };
 
-      ipv6PrefixLength = mkOption {
-        default = 64;
-        example = 64;
-        type = types.int;
+      ipv6.routes = mkOption {
+        default = [];
+        example = [
+          { address = "fdfd:b3f0::"; prefixLength = 48; }
+          { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
+        ];
+        type = with types; listOf (submodule (routeOpts 6));
         description = ''
-          Subnet mask of the interface, specified as the number of
-          bits in the prefix (<literal>64</literal>).
+          List of extra IPv6 static routes that will be assigned to the interface.
         '';
       };
 
@@ -273,11 +264,13 @@ let
       };
 
       virtualType = mkOption {
-        default = null;
-        type = with types; nullOr (enum [ "tun" "tap" ]);
+        default = if hasPrefix "tun" name then "tun" else "tap";
+        defaultText = literalExample ''if hasPrefix "tun" name then "tun" else "tap"'';
+        type = with types; enum [ "tun" "tap" ];
         description = ''
-          The explicit type of interface to create. Accepts tun or tap strings.
-          Also accepts null to implicitly detect the type of device.
+          The type of interface to create.
+          The default is TUN for an interface name starting
+          with "tun", otherwise TAP.
         '';
       };
 
@@ -305,6 +298,32 @@ let
       name = mkDefault name;
     };
 
+    # Renamed or removed options
+    imports =
+      let
+        defined = x: x != "_mkMergedOptionModule";
+      in [
+        (mkRenamedOptionModule [ "ip4" ] [ "ipv4" "addresses"])
+        (mkRenamedOptionModule [ "ip6" ] [ "ipv6" "addresses"])
+        (mkRemovedOptionModule [ "subnetMask" ] ''
+          Supply a prefix length instead; use option
+          networking.interfaces.<name>.ipv{4,6}.addresses'')
+        (mkMergedOptionModule
+          [ [ "ipAddress" ] [ "prefixLength" ] ]
+          [ "ipv4" "addresses" ]
+          (cfg: with cfg;
+            optional (defined ipAddress && defined prefixLength)
+            { address = ipAddress; prefixLength = prefixLength; }))
+        (mkMergedOptionModule
+          [ [ "ipv6Address" ] [ "ipv6PrefixLength" ] ]
+          [ "ipv6" "addresses" ]
+          (cfg: with cfg;
+            optional (defined ipv6Address && defined ipv6PrefixLength)
+            { address = ipv6Address; prefixLength = ipv6PrefixLength; }))
+
+        ({ options.warnings = options.warnings; })
+      ];
+
   };
 
   hexChars = stringToCharacters "0123456789abcdef";
@@ -441,7 +460,7 @@ in
     networking.interfaces = mkOption {
       default = {};
       example =
-        { eth0.ip4 = [ {
+        { eth0.ipv4.addresses = [ {
             address = "131.211.84.78";
             prefixLength = 25;
           } ];
@@ -920,13 +939,10 @@ in
 
   config = {
 
+    warnings = concatMap (i: i.warnings) interfaces;
+
     assertions =
       (flip map interfaces (i: {
-        assertion = i.subnetMask == null;
-        message = ''
-          The networking.interfaces."${i.name}".subnetMask option is defunct. Use prefixLength instead.
-        '';
-      })) ++ (flip map interfaces (i: {
         # With the linux kernel, interface name length is limited by IFNAMSIZ
         # to 16 bytes, including the trailing null byte.
         # See include/linux/if.h in the kernel sources
@@ -935,10 +951,15 @@ in
           The name of networking.interfaces."${i.name}" is too long, it needs to be less than 16 characters.
         '';
       })) ++ (flip map slaveIfs (i: {
-        assertion = i.ip4 == [ ] && i.ipAddress == null && i.ip6 == [ ] && i.ipv6Address == null;
+        assertion = i.ipv4.addresses == [ ] && i.ipv6.addresses == [ ];
         message = ''
           The networking.interfaces."${i.name}" must not have any defined ips when it is a slave.
         '';
+      })) ++ (flip map interfaces (i: {
+        assertion = i.preferTempAddress -> cfg.enableIPv6;
+        message = ''
+          Temporary addresses are only needed when IPv6 is enabled.
+        '';
       })) ++ [
         {
           assertion = cfg.hostId == null || (stringLength cfg.hostId == 8 && isHexString cfg.hostId);
@@ -961,9 +982,10 @@ in
       "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
-    } // listToAttrs (concatLists (flip map (filter (i: i.proxyARP) interfaces)
-        (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true))
-      ));
+    } // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces)
+        (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true)))
+      // listToAttrs (flip map (filter (i: i.preferTempAddress) interfaces)
+        (i: nameValuePair "net.ipv6.conf.${i.name}.use_tempaddr" 2));
 
     # Capabilities won't work unless we have at-least a 4.3 Linux
     # kernel because we need the ambient capability
@@ -1071,6 +1093,9 @@ in
           '' + optionalString (i.mtu != null) ''
             echo "setting MTU to ${toString i.mtu}..."
             ip link set "${i.name}" mtu "${toString i.mtu}"
+          '' + ''
+            echo -n "bringing up interface... "
+            ip link set "${i.name}" up && echo "done" || (echo "failed"; exit 1)
           '';
       })));
 
@@ -1104,7 +1129,7 @@ in
             # The script creates the required, new WLAN interfaces interfaces and configures the
             # existing, default interface.
             curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" ''
-              #!${pkgs.stdenv.shell}
+              #!${pkgs.runtimeShell}
               # Change the wireless phy device to a predictable name.
               ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device}
 
@@ -1123,7 +1148,7 @@ in
 
             # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface.
             newInterfaceScript = device: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" ''
-              #!${pkgs.stdenv.shell}
+              #!${pkgs.runtimeShell}
               # Configure the new interface
               ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type}
               ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${new.meshID}"}
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 7f5b55d5cca0..41dec2af9ed4 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -4,8 +4,11 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with import ../../lib/qemu-flags.nix { inherit pkgs; };
 
-let kernel = config.boot.kernelPackages.kernel; in
+let
+  kernel = config.boot.kernelPackages.kernel;
+in
 
 {
 
@@ -22,8 +25,8 @@ let kernel = config.boot.kernelPackages.kernel; in
 
     systemd.services.backdoor =
       { wantedBy = [ "multi-user.target" ];
-        requires = [ "dev-hvc0.device" "dev-ttyS0.device" ];
-        after = [ "dev-hvc0.device" "dev-ttyS0.device" ];
+        requires = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ];
+        after = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ];
         script =
           ''
             export USER=root
@@ -40,7 +43,7 @@ let kernel = config.boot.kernelPackages.kernel; in
 
             cd /tmp
             exec < /dev/hvc0 > /dev/hvc0
-            while ! exec 2> /dev/ttyS0; do sleep 0.1; done
+            while ! exec 2> /dev/${qemuSerialDevice}; do sleep 0.1; done
             echo "connecting to host..." >&2
             stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion
             echo
@@ -49,10 +52,10 @@ let kernel = config.boot.kernelPackages.kernel; in
         serviceConfig.KillSignal = "SIGHUP";
       };
 
-    # Prevent agetty from being instantiated on ttyS0, since it
-    # interferes with the backdoor (writes to ttyS0 will randomly fail
+    # Prevent agetty from being instantiated on the serial device, since it
+    # interferes with the backdoor (writes to it will randomly fail
     # with EIO).  Likewise for hvc0.
-    systemd.services."serial-getty@ttyS0".enable = false;
+    systemd.services."serial-getty@${qemuSerialDevice}".enable = false;
     systemd.services."serial-getty@hvc0".enable = false;
 
     boot.initrd.preDeviceCommands =
@@ -88,7 +91,7 @@ let kernel = config.boot.kernelPackages.kernel; in
     # Panic if an error occurs in stage 1 (rather than waiting for
     # user intervention).
     boot.kernelParams =
-      [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
+      [ "console=${qemuSerialDevice}" "panic=1" "boot.panic_on_fail" ];
 
     # `xwininfo' is used by the test driver to query open windows.
     environment.systemPackages = [ pkgs.xorg.xwininfo ];
@@ -122,7 +125,7 @@ let kernel = config.boot.kernelPackages.kernel; in
     # Make it easy to log in as root when running the test interactively.
     users.extraUsers.root.initialHashedPassword = mkOverride 150 "";
 
-    services.xserver.displayManager.logToJournal = true;
+    services.xserver.displayManager.job.logToJournal = true;
   };
 
 }
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index 1eb3ca707afd..f74c42a777f5 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -11,7 +11,7 @@ with lib;
 let cfg = config.ec2; in
 
 {
-  imports = [ ../profiles/headless.nix ./ec2-data.nix ./grow-partition.nix ./amazon-init.nix ];
+  imports = [ ../profiles/headless.nix ./ec2-data.nix ./amazon-init.nix ];
 
   config = {
 
@@ -21,7 +21,7 @@ let cfg = config.ec2; in
       }
     ];
 
-    virtualisation.growPartition = cfg.hvm;
+    boot.growPartition = cfg.hvm;
 
     fileSystems."/" = {
       device = "/dev/disk/by-label/nixos";
@@ -152,5 +152,8 @@ let cfg = config.ec2; in
     environment.systemPackages = [ pkgs.cryptsetup ];
 
     boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
+    
+    # EC2 has its own NTP server provided by the hypervisor
+    networking.timeServers = [ "169.254.169.123" ];
   };
 }
diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix
index a7362423eb46..8032b2c6d7ca 100644
--- a/nixos/modules/virtualisation/amazon-init.nix
+++ b/nixos/modules/virtualisation/amazon-init.nix
@@ -2,7 +2,7 @@
 
 let
   script = ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
 
     echo "attempting to fetch configuration from EC2 user data..."
 
diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix
index 6817eb837a01..b7ab54aab7ec 100644
--- a/nixos/modules/virtualisation/azure-agent.nix
+++ b/nixos/modules/virtualisation/azure-agent.nix
@@ -47,7 +47,7 @@ let
   };
 
   provisionedHook = pkgs.writeScript "provisioned-hook" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     ${config.systemd.package}/bin/systemctl start provisioned.target
   '';
 
@@ -66,6 +66,10 @@ in
       default = false;
       description = "Whether to enable verbose logging.";
     };
+    mountResourceDisk = mkOption {
+      default = true;
+      description = "Whether the agent should format (ext4) and mount the resource disk to /mnt/resource.";
+    };
   };
 
   ###### implementation
@@ -112,7 +116,7 @@ in
         Provisioning.ExecuteCustomData=n
 
         # Format if unformatted. If 'n', resource disk will not be mounted.
-        ResourceDisk.Format=y
+        ResourceDisk.Format=${if cfg.mountResourceDisk then "y" else "n"}
 
         # File system on the resource disk
         # Typically ext3 or ext4. FreeBSD images should use 'ufs2' here.
@@ -181,7 +185,7 @@ in
       after = [ "network-online.target" "sshd.service" ];
       wants = [ "network-online.target" ];
 
-      path = [ pkgs.e2fsprogs ];
+      path = [ pkgs.e2fsprogs pkgs.bash ];
       description = "Windows Azure Agent Service";
       unitConfig.ConditionPathExists = "/etc/waagent.conf";
       serviceConfig = {
diff --git a/nixos/modules/virtualisation/brightbox-image.nix b/nixos/modules/virtualisation/brightbox-image.nix
index 08bbcfd9d7c2..39a655b4c104 100644
--- a/nixos/modules/virtualisation/brightbox-image.nix
+++ b/nixos/modules/virtualisation/brightbox-image.nix
@@ -26,7 +26,7 @@ in
               rm $diskImageBase
               popd
             '';
-          diskImageBase = "nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw";
+          diskImageBase = "nixos-image-${config.system.nixos.label}-${pkgs.stdenv.system}.raw";
           buildInputs = [ pkgs.utillinux pkgs.perl ];
           exportReferencesGraph =
             [ "closure" config.system.build.toplevel ];
diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix
index b4f9d8b6fc17..5e368acd6d8b 100644
--- a/nixos/modules/virtualisation/container-config.nix
+++ b/nixos/modules/virtualisation/container-config.nix
@@ -11,7 +11,7 @@ with lib;
     services.udisks2.enable = mkDefault false;
     powerManagement.enable = mkDefault false;
 
-    networking.useHostResolvConf = true;
+    networking.useHostResolvConf = mkDefault true;
 
     # Containers should be light-weight, so start sshd on demand.
     services.openssh.startWhenNeeded = mkDefault true;
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index e68bfd860601..248c2fc1fb23 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -33,7 +33,7 @@ let
     in
       pkgs.writeScript "container-init"
       ''
-        #! ${pkgs.stdenv.shell} -e
+        #! ${pkgs.runtimeShell} -e
 
         # Initialise the container side of the veth pair.
         if [ "$PRIVATE_NETWORK" = 1 ]; then
@@ -112,7 +112,7 @@ let
 
       # If the host is 64-bit and the container is 32-bit, add a
       # --personality flag.
-      ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
+      ${optionalString (config.nixpkgs.localSystem.system == "x86_64-linux") ''
         if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
           extraFlags+=" --personality=x86"
         fi
@@ -223,7 +223,7 @@ let
   serviceDirectives = cfg: {
     ExecReload = pkgs.writeScript "reload-container"
       ''
-        #! ${pkgs.stdenv.shell} -e
+        #! ${pkgs.runtimeShell} -e
         ${pkgs.nixos-container}/bin/nixos-container run "$INSTANCE" -- \
           bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
       '';
@@ -255,7 +255,7 @@ let
   };
 
 
-  system = config.nixpkgs.system;
+  system = config.nixpkgs.localSystem.system;
 
   bindMountOpts = { name, config, ... }: {
 
@@ -575,6 +575,16 @@ in
               '';
             };
 
+            extraFlags = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "--drop-capability=CAP_SYS_CHROOT" ];
+              description = ''
+                Extra flags passed to the systemd-nspawn command.
+                See systemd-nspawn(1) for details.
+              '';
+            };
+
           } // networkOptions;
 
           config = mkMerge
@@ -714,7 +724,9 @@ in
             ${optionalString cfg.autoStart ''
               AUTO_START=1
             ''}
-            EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts}"
+            EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts +
+              optionalString (cfg.extraFlags != [])
+                (" " + concatStringsSep " " cfg.extraFlags)}"
           '';
       }) config.containers;
 
@@ -726,6 +738,11 @@ in
 
     networking.dhcpcd.denyInterfaces = [ "ve-*" "vb-*" ];
 
+    services.udev.extraRules = optionalString config.networking.networkmanager.enable ''
+      # Don't manage interfaces created by nixos-container.
+      ENV{INTERFACE}=="v[eb]-*", ENV{NM_UNMANAGED}="1"
+    '';
+
     environment.systemPackages = [ pkgs.nixos-container ];
   });
 }
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index 14826b6272f7..baffad79b001 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -223,21 +223,39 @@ let self = {
   "17.03".us-west-2.hvm-ebs = "ami-a93daac9";
   "17.03".us-west-2.hvm-s3 = "ami-5139ae31";
 
-  # 17.09.1483.d0f0657ca0
-  "17.09".eu-west-1.hvm-ebs = "ami-cf33e7b6";
-  "17.09".eu-west-2.hvm-ebs = "ami-7d061419";
-  "17.09".eu-central-1.hvm-ebs = "ami-7548fa1a";
-  "17.09".us-east-1.hvm-ebs = "ami-6f669d15";
-  "17.09".us-east-2.hvm-ebs = "ami-cbe1ccae";
-  "17.09".us-west-1.hvm-ebs = "ami-9d95a5fd";
-  "17.09".us-west-2.hvm-ebs = "ami-d3956fab";
-  "17.09".ca-central-1.hvm-ebs = "ami-ee4ef78a";
-  "17.09".ap-southeast-1.hvm-ebs = "ami-1dfc807e";
-  "17.09".ap-southeast-2.hvm-ebs = "ami-dcb350be";
-  "17.09".ap-northeast-1.hvm-ebs = "ami-00ec3d66";
-  "17.09".ap-northeast-2.hvm-ebs = "ami-1107dd7f";
-  "17.09".sa-east-1.hvm-ebs = "ami-0377086f";
-  "17.09".ap-south-1.hvm-ebs = "ami-4a064625";
+  # 17.09.2681.59661f21be6
+  "17.09".eu-west-1.hvm-ebs = "ami-a30192da";
+  "17.09".eu-west-2.hvm-ebs = "ami-295a414d";
+  "17.09".eu-west-3.hvm-ebs = "ami-8c0eb9f1";
+  "17.09".eu-central-1.hvm-ebs = "ami-266cfe49";
+  "17.09".us-east-1.hvm-ebs = "ami-40bee63a";
+  "17.09".us-east-2.hvm-ebs = "ami-9d84aff8";
+  "17.09".us-west-1.hvm-ebs = "ami-d14142b1";
+  "17.09".us-west-2.hvm-ebs = "ami-3eb40346";
+  "17.09".ca-central-1.hvm-ebs = "ami-ca8207ae";
+  "17.09".ap-southeast-1.hvm-ebs = "ami-84bccff8";
+  "17.09".ap-southeast-2.hvm-ebs = "ami-0dc5386f";
+  "17.09".ap-northeast-1.hvm-ebs = "ami-89b921ef";
+  "17.09".ap-northeast-2.hvm-ebs = "ami-179b3b79";
+  "17.09".sa-east-1.hvm-ebs = "ami-4762202b";
+  "17.09".ap-south-1.hvm-ebs = "ami-4e376021";
 
-  latest = self."17.09";
+  # 18.03.131792.becbe4dbe16
+  "18.03".eu-west-1.hvm-ebs = "ami-cda4fab4";
+  "18.03".eu-west-2.hvm-ebs = "ami-d96786be";
+  "18.03".eu-west-3.hvm-ebs = "ami-6b0cba16";
+  "18.03".eu-central-1.hvm-ebs = "ami-5e2b75b5";
+  "18.03".us-east-1.hvm-ebs = "ami-d464cba9";
+  "18.03".us-east-2.hvm-ebs = "ami-fd221298";
+  "18.03".us-west-1.hvm-ebs = "ami-ff0d1d9f";
+  "18.03".us-west-2.hvm-ebs = "ami-c05c3bb8";
+  "18.03".ca-central-1.hvm-ebs = "ami-cc72f4a8";
+  "18.03".ap-southeast-1.hvm-ebs = "ami-b61633ca";
+  "18.03".ap-southeast-2.hvm-ebs = "ami-530fc131";
+  "18.03".ap-northeast-1.hvm-ebs = "ami-90d6c0ec";
+  "18.03".ap-northeast-2.hvm-ebs = "ami-a1248bcf";
+  "18.03".sa-east-1.hvm-ebs = "ami-b090c6dc";
+  "18.03".ap-south-1.hvm-ebs = "ami-32c9ec5d";
+
+  latest = self."18.03";
 }; in self
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index e3b3e6a5f4ab..0b6bec786da4 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -2,11 +2,11 @@
 
 with lib;
 let
-  diskSize = 1024; # MB
+  diskSize = 1536; # MB
   gce = pkgs.google-compute-engine;
 in
 {
-  imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ./grow-partition.nix ];
+  imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ];
 
   system.build.googleComputeImage = import ../../lib/make-disk-image.nix {
     name = "google-compute-image";
@@ -14,7 +14,7 @@ in
       PATH=$PATH:${pkgs.stdenv.lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]}
       pushd $out
       mv $diskImage disk.raw
-      tar -Szcf nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw.tar.gz disk.raw
+      tar -Szcf nixos-image-${config.system.nixos.label}-${pkgs.stdenv.system}.raw.tar.gz disk.raw
       rm $out/disk.raw
       popd
     '';
@@ -29,6 +29,7 @@ in
     autoResize = true;
   };
 
+  boot.growPartition = true;
   boot.kernelParams = [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
   boot.initrd.kernelModules = [ "virtio_scsi" ];
   boot.kernelModules = [ "virtio_pci" "virtio_net" ];
@@ -56,6 +57,12 @@ in
   # Always include cryptsetup so that NixOps can use it.
   environment.systemPackages = [ pkgs.cryptsetup ];
 
+  # Make sure GCE image does not replace host key that NixOps sets
+  environment.etc."default/instance_configs.cfg".text = lib.mkDefault ''
+    [InstanceSetup]
+    set_host_keys = false
+  '';
+
   # Rely on GCP's firewall instead
   networking.firewall.enable = mkDefault false;
 
@@ -68,6 +75,9 @@ in
 
   networking.usePredictableInterfaceNames = false;
 
+  # GC has 1460 MTU
+  networking.interfaces.eth0.mtu = 1460;
+
   # allow the google-accounts-daemon to manage users
   users.mutableUsers = true;
   # and allow users to sudo without password
@@ -211,7 +221,7 @@ in
           echo "Obtaining SSH keys..."
           mkdir -m 0700 -p /root/.ssh
           AUTH_KEYS=$(${mktemp})
-          ${wget} -O $AUTH_KEYS http://metadata.google.internal/computeMetadata/v1/project/attributes/sshKeys
+          ${wget} -O $AUTH_KEYS --header="Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/attributes/sshKeys
           if [ -s $AUTH_KEYS ]; then
 
             # Read in key one by one, split in case Google decided
diff --git a/nixos/modules/virtualisation/grow-partition.nix b/nixos/modules/virtualisation/grow-partition.nix
index 2cb932d208f0..444c0bc1630e 100644
--- a/nixos/modules/virtualisation/grow-partition.nix
+++ b/nixos/modules/virtualisation/grow-partition.nix
@@ -1,48 +1,3 @@
-# This module automatically grows the root partition on virtual machines.
-# This allows an instance to be created with a bigger root filesystem
-# than provided by the machine image.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-
-  options = {
-
-    virtualisation.growPartition = mkOption {
-      type = types.bool;
-      default = true;
-    };
-
-  };
-
-  config = mkIf config.virtualisation.growPartition {
-
-    boot.initrd.extraUtilsCommands = ''
-      copy_bin_and_libs ${pkgs.gawk}/bin/gawk
-      copy_bin_and_libs ${pkgs.gnused}/bin/sed
-      copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
-      copy_bin_and_libs ${pkgs.utillinux}/sbin/lsblk
-
-      substitute "${pkgs.cloud-utils}/bin/.growpart-wrapped" "$out/bin/growpart" \
-        --replace "${pkgs.bash}/bin/sh" "/bin/sh" \
-        --replace "awk" "gawk" \
-        --replace "sed" "gnused"
-
-      ln -s sed $out/bin/gnused
-    '';
-
-    boot.initrd.postDeviceCommands = ''
-      rootDevice="${config.fileSystems."/".device}"
-      if [ -e "$rootDevice" ]; then
-        rootDevice="$(readlink -f "$rootDevice")"
-        parentDevice="$(lsblk -npo PKNAME "$rootDevice")"
-        TMPDIR=/run sh $(type -P growpart) "$parentDevice" "''${rootDevice#$parentDevice}"
-        udevadm settle
-      fi
-    '';
-
-  };
-
-}
+# This profile is deprecated, use boot.growPartition directly.
+builtins.trace "the profile <nixos/modules/virtualisation/grow-partition.nix> is deprecated, use boot.growPartition instead"
+{ }
diff --git a/nixos/modules/virtualisation/hyperv-guest.nix b/nixos/modules/virtualisation/hyperv-guest.nix
new file mode 100644
index 000000000000..ecd2a8117710
--- /dev/null
+++ b/nixos/modules/virtualisation/hyperv-guest.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.virtualisation.hypervGuest;
+
+in {
+  options = {
+    virtualisation.hypervGuest = {
+      enable = mkEnableOption "Hyper-V Guest Support";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ config.boot.kernelPackages.hyperv-daemons.bin ];
+
+    security.rngd.enable = false;
+
+    # enable hotadding memory
+    services.udev.packages = lib.singleton (pkgs.writeTextFile {
+      name = "hyperv-memory-hotadd-udev-rules";
+      destination = "/etc/udev/rules.d/99-hyperv-memory-hotadd.rules";
+      text = ''
+        ACTION="add", SUBSYSTEM=="memory", ATTR{state}="online"
+      '';
+    });
+
+    systemd = {
+      packages = [ config.boot.kernelPackages.hyperv-daemons.lib ];
+
+      targets.hyperv-daemons = {
+        wantedBy = [ "multi-user.target" ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 8aa7ad8e3911..024db7f87c2e 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -37,11 +37,13 @@ in {
       '';
     };
 
-    virtualisation.libvirtd.enableKVM = mkOption {
-      type = types.bool;
-      default = true;
+    virtualisation.libvirtd.qemuPackage = mkOption {
+      type = types.package;
+      default = pkgs.qemu;
       description = ''
-        This option enables support for QEMU/KVM in libvirtd.
+        Qemu package to use with libvirt.
+        `pkgs.qemu` can emulate alien architectures (e.g. aarch64 on x86)
+        `pkgs.qemu_kvm` saves disk space allowing to emulate only host architectures.
       '';
     };
 
@@ -102,7 +104,7 @@ in {
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = with pkgs; [ libvirt netcat-openbsd qemu_kvm ];
+    environment.systemPackages = with pkgs; [ libvirt netcat-openbsd cfg.qemuPackage ];
 
     boot.kernelModules = [ "tun" ];
 
@@ -117,17 +119,10 @@ in {
       after = [ "systemd-udev-settle.service" ]
               ++ optional vswitch.enable "vswitchd.service";
 
-      environment = {
-        LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
-      };
+      environment.LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
 
-      path = with pkgs; [
-          bridge-utils
-          dmidecode
-          dnsmasq
-          ebtables
-        ]
-        ++ optional vswitch.enable vswitch.package;
+      path = [ cfg.qemuPackage ] # libvirtd requires qemu-img to manage disk images
+             ++ optional vswitch.enable vswitch.package;
 
       preStart = ''
         mkdir -p /var/log/libvirt/qemu -m 755
@@ -154,9 +149,9 @@ in {
 
         # stable (not GC'able as in /nix/store) paths for using in <emulator> section of xml configs
         mkdir -p /run/libvirt/nix-emulators
-        ln -s --force ${pkgs.libvirt}/libexec/libvirt_lxc /run/libvirt/nix-emulators/
-        ${optionalString pkgs.stdenv.isAarch64 "ln -s --force ${pkgs.qemu}/bin/qemu-system-aarch64 /run/libvirt/nix-emulators/"}
-        ${optionalString cfg.enableKVM         "ln -s --force ${pkgs.qemu_kvm}/bin/qemu-kvm        /run/libvirt/nix-emulators/"}
+        for emulator in ${pkgs.libvirt}/libexec/libvirt_lxc ${cfg.qemuPackage}/bin/qemu-kvm ${cfg.qemuPackage}/bin/qemu-system-*; do
+          ln -s --force "$emulator" /run/libvirt/nix-emulators/
+        done
 
         ${optionalString cfg.qemuOvmf ''
             mkdir -p /run/libvirt/nix-ovmf
diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
index 2310fe984325..9b5adaf08249 100644
--- a/nixos/modules/virtualisation/lxc.nix
+++ b/nixos/modules/virtualisation/lxc.nix
@@ -74,6 +74,9 @@ in
     systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
 
     security.apparmor.packages = [ pkgs.lxc ];
-    security.apparmor.profiles = [ "${pkgs.lxc}/etc/apparmor.d/lxc-containers" ];
+    security.apparmor.profiles = [
+      "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
+      "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
+    ];
   };
 }
diff --git a/nixos/modules/virtualisation/lxcfs.nix b/nixos/modules/virtualisation/lxcfs.nix
index 48462dc66da8..b2457403463a 100644
--- a/nixos/modules/virtualisation/lxcfs.nix
+++ b/nixos/modules/virtualisation/lxcfs.nix
@@ -28,13 +28,9 @@ in {
 
   ###### implementation
   config = mkIf cfg.enable {
-    services.cgmanager.enable = true;
-
     systemd.services.lxcfs = {
       description = "FUSE filesystem for LXC";
       wantedBy = [ "multi-user.target" ];
-      requires = [ "cgmanager.service" ];
-      after = [ "cgmanager.service" ];
       before = [ "lxc.service" ];
       restartIfChanged = false;
       serviceConfig = {
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
index b1ff0337994e..3e76cdacfc4b 100644
--- a/nixos/modules/virtualisation/lxd.nix
+++ b/nixos/modules/virtualisation/lxd.nix
@@ -15,50 +15,69 @@ in
 
   options = {
 
-    virtualisation.lxd.enable =
-      mkOption {
+    virtualisation.lxd = {
+      enable = mkOption {
         type = types.bool;
         default = false;
-        description =
-          ''
-            This option enables lxd, a daemon that manages
-            containers. Users in the "lxd" group can interact with
-            the daemon (e.g. to start or stop containers) using the
-            <command>lxc</command> command line tool, among others.
-          '';
+        description = ''
+          This option enables lxd, a daemon that manages
+          containers. Users in the "lxd" group can interact with
+          the daemon (e.g. to start or stop containers) using the
+          <command>lxc</command> command line tool, among others.
+        '';
       };
-
+      zfsSupport = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          enables lxd to use zfs as a storage for containers.
+          This option is enabled by default if a zfs pool is configured
+          with nixos.
+        '';
+      };
+    };
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages =
-      [ pkgs.lxd ];
+    environment.systemPackages = [ pkgs.lxd ];
 
-    systemd.services.lxd =
-      { description = "LXD Container Management Daemon";
+    security.apparmor = {
+      enable = true;
+      profiles = [
+        "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
+        "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
+      ];
+      packages = [ pkgs.lxc ];
+    };
 
-        wantedBy = [ "multi-user.target" ];
-        after = [ "systemd-udev-settle.service" ];
+    systemd.services.lxd = {
+      description = "LXD Container Management Daemon";
 
-        # TODO(wkennington): Add lvm2 and thin-provisioning-tools
-        path = with pkgs; [ acl rsync gnutar xz btrfs-progs gzip dnsmasq squashfsTools iproute iptables ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-udev-settle.service" ];
 
-        serviceConfig.ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --syslog --group lxd";
-        serviceConfig.Type = "simple";
-        serviceConfig.KillMode = "process"; # when stopping, leave the containers alone
+      path = lib.optional cfg.zfsSupport pkgs.zfs;
+
+      preStart = ''
+        mkdir -m 0755 -p /var/lib/lxc/rootfs
+      '';
+
+      serviceConfig = {
+        ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --group lxd";
+        Type = "simple";
+        KillMode = "process"; # when stopping, leave the containers alone
       };
 
+    };
+
     users.extraGroups.lxd.gid = config.ids.gids.lxd;
 
     users.extraUsers.root = {
       subUidRanges = [ { startUid = 1000000; count = 65536; } ];
       subGidRanges = [ { startGid = 1000000; count = 65536; } ];
     };
-
   };
-
 }
diff --git a/nixos/modules/virtualisation/nova-config.nix b/nixos/modules/virtualisation/nova-config.nix
index c865cf451e40..c1d2a314daf2 100644
--- a/nixos/modules/virtualisation/nova-config.nix
+++ b/nixos/modules/virtualisation/nova-config.nix
@@ -6,7 +6,6 @@ with lib;
   imports = [
     ../profiles/qemu-guest.nix
     ../profiles/headless.nix
-    ./grow-partition.nix
   ];
 
   config = {
@@ -15,8 +14,7 @@ with lib;
       autoResize = true;
     };
 
-    virtualisation.growPartition = true;
-
+    boot.growPartition = true;
     boot.kernelParams = [ "console=ttyS0" ];
     boot.loader.grub.device = "/dev/vda";
     boot.loader.timeout = 0;
diff --git a/nixos/modules/virtualisation/nova.nix b/nixos/modules/virtualisation/nova.nix
deleted file mode 100644
index c2837d0e2e24..000000000000
--- a/nixos/modules/virtualisation/nova.nix
+++ /dev/null
@@ -1,174 +0,0 @@
-# Module for Nova, a.k.a. OpenStack Compute.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.virtualisation.nova;
-
-  nova = pkgs.nova;
-
-  novaConf = pkgs.writeText "nova.conf"
-    ''
-      --nodaemon
-      --verbose
-      ${cfg.extraConfig}
-    '';
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    virtualisation.nova.enableSingleNode =
-      mkOption {
-        default = false;
-        description =
-          ''
-            This option enables Nova, also known as OpenStack Compute,
-            a cloud computing system, as a single-machine
-            installation.  That is, all of Nova's components are
-            enabled on this machine, using SQLite as Nova's database.
-            This is useful for evaluating and experimenting with Nova.
-            However, for a real cloud computing environment, you'll
-            want to enable some of Nova's services on other machines,
-            and use a database such as MySQL.
-          '';
-      };
-
-    virtualisation.nova.extraConfig =
-      mkOption {
-        default = "";
-        description =
-          ''
-            Additional text appended to <filename>nova.conf</filename>,
-            the main Nova configuration file.
-          '';
-      };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enableSingleNode {
-
-    environment.systemPackages = [ nova pkgs.euca2ools pkgs.novaclient ];
-
-    environment.etc =
-      [ { source = novaConf;
-          target = "nova/nova.conf";
-        }
-      ];
-
-    # Nova requires libvirtd and RabbitMQ.
-    virtualisation.libvirtd.enable = true;
-    services.rabbitmq.enable = true;
-
-    # `qemu-nbd' required the `nbd' kernel module.
-    boot.kernelModules = [ "nbd" ];
-
-    system.activationScripts.nova =
-      ''
-        mkdir -m 755 -p /var/lib/nova
-        mkdir -m 755 -p /var/lib/nova/networks
-        mkdir -m 700 -p /var/lib/nova/instances
-        mkdir -m 700 -p /var/lib/nova/keys
-
-        # Allow the CA certificate generation script (called by
-        # nova-api) to work.
-        mkdir -m 700 -p /var/lib/nova/CA /var/lib/nova/CA/private
-
-        # Initialise the SQLite database.
-        ${nova}/bin/nova-manage db sync
-      '';
-
-    # `nova-api' receives and executes external client requests from
-    # tools such as euca2ools.  It listens on port 8773 (XML) and 8774
-    # (JSON).
-    jobs.nova_api =
-      { name = "nova-api";
-
-        description = "Nova API service";
-
-        startOn = "ip-up";
-
-        # `openssl' is required to generate the CA.  `openssh' is
-        # required to generate key pairs.
-        path = [ pkgs.openssl config.programs.ssh.package pkgs.bash ];
-
-        respawn = false;
-
-        exec = "${nova}/bin/nova-api --flagfile=${novaConf} --api_paste_config=${nova}/etc/nova/api-paste.ini";
-      };
-
-    # `nova-objectstore' is a simple image server.  Useful if you're
-    # not running the OpenStack Imaging Service (Swift).  It serves
-    # images placed in /var/lib/nova/images/.
-    jobs.nova_objectstore =
-      { name = "nova-objectstore";
-
-        description = "Nova Simple Object Store Service";
-
-        startOn = "ip-up";
-
-        preStart =
-          ''
-            mkdir -m 700 -p /var/lib/nova/images
-          '';
-
-        exec = "${nova}/bin/nova-objectstore --flagfile=${novaConf}";
-      };
-
-    # `nova-scheduler' schedules VM execution requests.
-    jobs.nova_scheduler =
-      { name = "nova-scheduler";
-
-        description = "Nova Scheduler Service";
-
-        startOn = "ip-up";
-
-        exec = "${nova}/bin/nova-scheduler --flagfile=${novaConf}";
-      };
-
-    # `nova-compute' starts and manages virtual machines.
-    jobs.nova_compute =
-      { name = "nova-compute";
-
-        description = "Nova Compute Service";
-
-        startOn = "ip-up";
-
-        path =
-          [ pkgs.sudo pkgs.vlan pkgs.nettools pkgs.iptables pkgs.qemu_kvm
-            pkgs.e2fsprogs pkgs.utillinux pkgs.multipath-tools pkgs.iproute
-            pkgs.bridge-utils
-          ];
-
-        exec = "${nova}/bin/nova-compute --flagfile=${novaConf}";
-      };
-
-    # `nova-network' manages networks and allocates IP addresses.
-    jobs.nova_network =
-      { name = "nova-network";
-
-        description = "Nova Network Service";
-
-        startOn = "ip-up";
-
-        path =
-          [ pkgs.sudo pkgs.vlan pkgs.dnsmasq pkgs.nettools pkgs.iptables
-            pkgs.iproute pkgs.bridge-utils pkgs.radvd
-          ];
-
-        exec = "${nova}/bin/nova-network --flagfile=${novaConf}";
-      };
-
-  };
-
-}
diff --git a/nixos/modules/virtualisation/openstack/common.nix b/nixos/modules/virtualisation/openstack/common.nix
deleted file mode 100644
index 2feb0a873951..000000000000
--- a/nixos/modules/virtualisation/openstack/common.nix
+++ /dev/null
@@ -1,84 +0,0 @@
-{ lib }:
-
-with lib;
-
-rec {
-  # A shell script string helper to get the value of a secret at
-  # runtime.
-  getSecret = secretOption:
-    if secretOption.storage == "fromFile"
-    then ''$(cat ${secretOption.value})''
-    else ''${secretOption.value}'';
-
-
-  # A shell script string help to replace at runtime in a file the
-  # pattern of a secret by its value.
-  replaceSecret = secretOption: filename: ''
-    sed -i "s/${secretOption.pattern}/${getSecret secretOption}/g" ${filename}
-    '';
-
-  # This generates an option that can be used to declare secrets which
-  # can be stored in the nix store, or not. A pattern is written in
-  # the nix store to represent the secret. The pattern can
-  # then be overwritten with the value of the secret at runtime.
-  mkSecretOption = {name, description ? ""}:
-    mkOption {
-      description = description;
-      type = types.submodule ({
-        options = {
-          pattern = mkOption {
-            type = types.str;
-            default = "##${name}##";
-            description = "The pattern that represent the secret.";
-            };
-          storage = mkOption {
-            type = types.enum [ "fromNixStore" "fromFile" ];
-            description = ''
-            Choose the way the password is provisionned. If
-            fromNixStore is used, the value is the password and it is
-            written in the nix store. If fromFile is used, the value
-            is a path from where the password will be read at
-            runtime. This is generally used with <link
-            xlink:href="https://nixos.org/nixops/manual/#opt-deployment.keys">
-            deployment keys</link> of Nixops.
-           '';};
-            value = mkOption {
-              type = types.str;
-	      description = ''
-	      If the storage is fromNixStore, the value is the password itself,
-	      otherwise it is a path to the file that contains the password.
-	      '';
-	      };
-            };});
-  };
-  
-  databaseOption = name: {
-    host = mkOption {
-      type = types.str;
-      default = "localhost";
-      description = ''
-        Host of the database.
-      '';
-    };
-
-    name = mkOption {
-      type = types.str;
-      default = name;
-      description = ''
-        Name of the existing database.
-      '';
-    };
-
-    user = mkOption {
-      type = types.str;
-      default = name;
-      description = ''
-        The database user. The user must exist and has access to
-        the specified database.
-      '';
-    };
-    password = mkSecretOption {
-      name = name + "MysqlPassword";
-      description = "The database user's password";};
-  };
-}
diff --git a/nixos/modules/virtualisation/openstack/glance.nix b/nixos/modules/virtualisation/openstack/glance.nix
deleted file mode 100644
index 7862409a65ec..000000000000
--- a/nixos/modules/virtualisation/openstack/glance.nix
+++ /dev/null
@@ -1,245 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib; with import ./common.nix {inherit lib;};
-
-let
-  cfg = config.virtualisation.openstack.glance;
-  commonConf = ''
-    [database]
-    connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
-    notification_driver = noop
-
-    [keystone_authtoken]
-    auth_url = ${cfg.authUrl}
-    auth_plugin = password
-    project_name = service
-    project_domain_id = default
-    user_domain_id = default
-    username = ${cfg.serviceUsername}
-    password = ${cfg.servicePassword.pattern}
-
-    [glance_store]
-    default_store = file
-    filesystem_store_datadir = /var/lib/glance/images/
-  '';
-  glanceApiConfTpl = pkgs.writeText "glance-api.conf" ''
-    ${commonConf}
-
-    [paste_deploy]
-    flavor = keystone
-    config_file = ${cfg.package}/etc/glance-api-paste.ini
-  '';
-  glanceRegistryConfTpl = pkgs.writeText "glance-registry.conf" ''
-    ${commonConf}
-
-    [paste_deploy]
-    config_file = ${cfg.package}/etc/glance-registry-paste.ini
-  '';
-  glanceApiConf = "/var/lib/glance/glance-api.conf";
-  glanceRegistryConf = "/var/lib/glance/glance-registry.conf";
-
-in {
-  options.virtualisation.openstack.glance = {
-    package = mkOption {
-      type = types.package;
-      default = pkgs.glance;
-      defaultText = "pkgs.glance";
-      description = ''
-        Glance package to use.
-      '';
-    };
-
-    enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        This option enables Glance as a single-machine
-        installation. That is, all of Glance's components are
-        enabled on this machine. This is useful for evaluating and
-        experimenting with Glance. Note we are currently not
-        providing any configurations for a multi-node setup.
-      '';
-    };
-
-    authUrl = mkOption {
-      type = types.str;
-      default = http://localhost:5000;
-      description = ''
-        Complete public Identity (Keystone) API endpoint. Note this is
-        unversionned.
-      '';
-    };
-
-    serviceUsername = mkOption {
-      type = types.str;
-      default = "glance";
-      description = ''
-        The Glance service username. This user is created if bootstrap
-        is enable, otherwise it has to be manually created before
-        starting this service.
-      '';
-    };
-
-    servicePassword = mkSecretOption {
-      name = "glanceAdminPassword";
-      description = ''
-        The Glance service user's password.
-      '';
-    };
-
-    database = databaseOption "glance";
-
-    bootstrap = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Bootstrap the Glance service by creating the service tenant,
-          an admin account and a public endpoint. This option provides
-          a ready-to-use glance service. This is only done at the
-          first Glance execution by the systemd post start section.
-          The keystone admin account is used to create required
-          Keystone resource for the Glance service.
-
-          <note><para> This option is a helper for setting up
-          development or testing environments.</para></note>
-        '';
-      };
-
-      endpointPublic = mkOption {
-        type = types.str;
-        default = "http://localhost:9292";
-        description = ''
-          The public image endpoint. The link <link
-          xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
-          create endpoint</link> provides more informations
-          about that.
-        '';
-      };
-
-      keystoneAdminUsername = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          The keystone admin user name used to create the Glance account.
-        '';
-      };
-
-      keystoneAdminPassword = mkSecretOption {
-        name = "keystoneAdminPassword";
-        description = ''
-          The keystone admin user's password.
-        '';
-      };
-
-      keystoneAdminTenant = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          The keystone admin tenant used to create the Glance account.
-        '';
-      };
-      keystoneAuthUrl = mkOption {
-        type = types.str;
-        default = "http://localhost:5000/v2.0";
-        description = ''
-          The keystone auth url used to create the Glance account.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    users.extraUsers = [{
-      name = "glance";
-      group = "glance";
-      uid = config.ids.gids.glance;
-
-    }];
-    users.extraGroups = [{
-      name = "glance";
-      gid = config.ids.gids.glance;
-    }];
-
-    systemd.services.glance-registry = {
-      description = "OpenStack Glance Registry Daemon";
-      after = [ "network.target"];
-      path = [ pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 775 -p /var/lib/glance/{images,scrubber,image_cache}
-        chown glance:glance /var/lib/glance/{images,scrubber,image_cache}
-
-        # Secret file managment
-        cp ${glanceRegistryConfTpl} ${glanceRegistryConf};
-        chown glance:glance ${glanceRegistryConf};
-        chmod 640 ${glanceRegistryConf}
-        ${replaceSecret cfg.database.password glanceRegistryConf}
-        ${replaceSecret cfg.servicePassword glanceRegistryConf}
-
-        cp ${glanceApiConfTpl} ${glanceApiConf};
-        chown glance:glance ${glanceApiConf};
-        chmod 640 ${glanceApiConf}
-        ${replaceSecret cfg.database.password glanceApiConf}
-        ${replaceSecret cfg.servicePassword glanceApiConf}
-
-        # Initialise the database
-        ${cfg.package}/bin/glance-manage --config-file=${glanceApiConf} --config-file=${glanceRegistryConf} db_sync
-      '';
-      postStart = ''
-        set -eu
-        export OS_AUTH_URL=${cfg.bootstrap.keystoneAuthUrl}
-        export OS_USERNAME=${cfg.bootstrap.keystoneAdminUsername}
-        export OS_PASSWORD=${getSecret cfg.bootstrap.keystoneAdminPassword}
-        export OS_TENANT_NAME=${cfg.bootstrap.keystoneAdminTenant}
-
-        # Wait until the keystone is available for use
-        count=0
-        while ! keystone user-get ${cfg.bootstrap.keystoneAdminUsername} > /dev/null
-        do
-            if [ $count -eq 30 ]
-            then
-                echo "Tried 30 times, giving up..."
-                exit 1
-            fi
-
-            echo "Keystone not yet started. Waiting for 1 second..."
-            count=$((count++))
-            sleep 1
-        done
-
-        # If the service glance doesn't exist, we consider glance is
-        # not initialized
-        if ! keystone service-get glance
-        then
-            keystone service-create --type image --name glance
-            ID=$(keystone service-get glance | awk '/ id / { print $4 }')
-            keystone endpoint-create --region RegionOne --service $ID --internalurl http://localhost:9292 --adminurl http://localhost:9292 --publicurl ${cfg.bootstrap.endpointPublic}
-
-            keystone user-create --name ${cfg.serviceUsername} --tenant service --pass ${getSecret cfg.servicePassword}
-            keystone user-role-add --tenant service --user ${cfg.serviceUsername} --role admin
-        fi
-        '';
-      serviceConfig = {
-        PermissionsStartOnly = true; # preStart must be run as root
-        TimeoutStartSec = "600"; # 10min for initial db migrations
-        User = "glance";
-        Group = "glance";
-        ExecStart = "${cfg.package}/bin/glance-registry --config-file=${glanceRegistryConf}";
-      };
-    };
-    systemd.services.glance-api = {
-      description = "OpenStack Glance API Daemon";
-      after = [ "glance-registry.service" "network.target"];
-      requires = [ "glance-registry.service" "network.target"]; 
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        PermissionsStartOnly = true; # preStart must be run as root
-        User = "glance";
-        Group = "glance";
-        ExecStart = "${cfg.package}/bin/glance-api --config-file=${glanceApiConf}";
-      };
-    };
-  };
-
-}
diff --git a/nixos/modules/virtualisation/openstack/keystone.nix b/nixos/modules/virtualisation/openstack/keystone.nix
deleted file mode 100644
index e32c5a4cae1b..000000000000
--- a/nixos/modules/virtualisation/openstack/keystone.nix
+++ /dev/null
@@ -1,220 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib; with import ./common.nix {inherit lib;};
-
-let
-  cfg = config.virtualisation.openstack.keystone;
-  keystoneConfTpl = pkgs.writeText "keystone.conf" ''
-    [DEFAULT]
-    admin_token = ${cfg.adminToken.pattern}
-    policy_file=${cfg.package}/etc/policy.json
-
-    [database]
-
-    connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
-
-    [paste_deploy]
-    config_file = ${cfg.package}/etc/keystone-paste.ini
-
-    ${cfg.extraConfig}
-  '';
-  keystoneConf = "/var/lib/keystone/keystone.conf";
-
-in {
-  options.virtualisation.openstack.keystone = {
-    package = mkOption {
-      type = types.package;
-      example = literalExample "pkgs.keystone";
-      description = ''
-        Keystone package to use.
-      '';
-    };
-
-    enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Enable Keystone, the OpenStack Identity Service
-      '';
-    };
-
-    extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      description = ''
-        Additional text appended to <filename>keystone.conf</filename>,
-        the main Keystone configuration file.
-      '';
-    };
-
-    adminToken = mkSecretOption {
-      name = "adminToken";
-      description = ''
-        This is the admin token used to boostrap keystone,
-        ie. to provision first resources.
-      '';
-    };
-
-    bootstrap = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Bootstrap the Keystone service by creating the service
-          tenant, an admin account and a public endpoint. This options
-          provides a ready-to-use admin account. This is only done at
-          the first Keystone execution by the systemd post start.
-
-          Note this option is a helper for setting up development or
-          testing environments.
-        '';
-      };
-
-      endpointPublic = mkOption {
-        type = types.str;
-        default = "http://localhost:5000/v2.0";
-        description = ''
-          The public identity endpoint. The link <link
-          xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
-          create keystone endpoint</link> provides more informations
-          about that.
-        '';
-      };
-
-      adminUsername = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          A keystone admin username.
-        '';
-      };
-
-      adminPassword = mkSecretOption {
-        name = "keystoneAdminPassword";
-        description = ''
-          The keystone admin user's password.
-        '';
-      };
-
-      adminTenant = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          A keystone admin tenant name.
-        '';
-      };
-    };
-
-    database = {
-      host = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = ''
-          Host of the database.
-        '';
-      };
-
-      name = mkOption {
-        type = types.str;
-        default = "keystone";
-        description = ''
-          Name of the existing database.
-        '';
-      };
-
-      user = mkOption {
-        type = types.str;
-        default = "keystone";
-        description = ''
-          The database user. The user must exist and has access to
-          the specified database.
-        '';
-      };
-      password = mkSecretOption {
-        name = "mysqlPassword";
-        description = "The database user's password";};
-    };
-  };
-
-  config = mkIf cfg.enable {
-    # Note: when changing the default, make it conditional on
-    # ‘system.stateVersion’ to maintain compatibility with existing
-    # systems!
-    virtualisation.openstack.keystone.package = mkDefault pkgs.keystone;
-
-    users.extraUsers = [{
-      name = "keystone";
-      group = "keystone";
-      uid = config.ids.uids.keystone;
-    }];
-    users.extraGroups = [{
-      name = "keystone";
-      gid = config.ids.gids.keystone;
-    }];
-
-    systemd.services.keystone-all = {
-        description = "OpenStack Keystone Daemon";
-        after = [ "network.target"];
-        path = [ cfg.package pkgs.mysql pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
-        wantedBy = [ "multi-user.target" ];
-        preStart = ''
-          mkdir -m 755 -p /var/lib/keystone
-
-          cp ${keystoneConfTpl} ${keystoneConf};
-          chown keystone:keystone ${keystoneConf};
-          chmod 640 ${keystoneConf}
-
-          ${replaceSecret cfg.database.password keystoneConf}
-          ${replaceSecret cfg.adminToken keystoneConf}
-
-          # Initialise the database
-          ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} db_sync
-          # Set up the keystone's PKI infrastructure
-          ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} pki_setup --keystone-user keystone --keystone-group keystone
-        '';
-        postStart = optionalString cfg.bootstrap.enable ''
-          set -eu
-          # Wait until the keystone is available for use
-          count=0
-          while ! curl --fail -s  http://localhost:35357/v2.0 > /dev/null 
-          do
-              if [ $count -eq 30 ]
-              then
-                  echo "Tried 30 times, giving up..."
-                  exit 1
-              fi
-
-              echo "Keystone not yet started. Waiting for 1 second..."
-              count=$((count++))
-              sleep 1
-          done
-
-          # We use the service token to create a first admin user
-          export OS_SERVICE_ENDPOINT=http://localhost:35357/v2.0
-          export OS_SERVICE_TOKEN=${getSecret cfg.adminToken}
-
-          # If the tenant service doesn't exist, we consider
-          # keystone is not initialized
-          if ! keystone tenant-get service
-          then
-              keystone tenant-create --name service
-              keystone tenant-create --name ${cfg.bootstrap.adminTenant}
-              keystone user-create --name ${cfg.bootstrap.adminUsername} --tenant ${cfg.bootstrap.adminTenant} --pass ${getSecret cfg.bootstrap.adminPassword}
-              keystone role-create --name admin
-              keystone role-create --name Member
-              keystone user-role-add --tenant ${cfg.bootstrap.adminTenant} --user ${cfg.bootstrap.adminUsername} --role admin
-              keystone service-create --type identity --name keystone
-              ID=$(keystone service-get keystone | awk '/ id / { print $4 }')
-              keystone endpoint-create --region RegionOne --service $ID --publicurl ${cfg.bootstrap.endpointPublic} --adminurl http://localhost:35357/v2.0 --internalurl http://localhost:5000/v2.0
-          fi
-        '';
-        serviceConfig = {
-          PermissionsStartOnly = true; # preStart must be run as root
-          TimeoutStartSec = "600"; # 10min for initial db migrations
-          User = "keystone";
-          Group = "keystone";
-          ExecStart = "${cfg.package}/bin/keystone-all --config-file=${keystoneConf}";
-        };
-      };
-  };
-}
diff --git a/nixos/modules/virtualisation/openvswitch.nix b/nixos/modules/virtualisation/openvswitch.nix
index 4218a3840fc1..38b138e06326 100644
--- a/nixos/modules/virtualisation/openvswitch.nix
+++ b/nixos/modules/virtualisation/openvswitch.nix
@@ -169,7 +169,7 @@ in {
         mkdir -p ${runDir}/ipsec/{etc/racoon,etc/init.d/,usr/sbin/}
         ln -fs ${pkgs.ipsecTools}/bin/setkey ${runDir}/ipsec/usr/sbin/setkey
         ln -fs ${pkgs.writeScript "racoon-restart" ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         /var/run/current-system/sw/bin/systemctl $1 racoon
         ''} ${runDir}/ipsec/etc/init.d/racoon
       '';
diff --git a/nixos/modules/virtualisation/parallels-guest.nix b/nixos/modules/virtualisation/parallels-guest.nix
index bd85973ee561..36ca7f356d44 100644
--- a/nixos/modules/virtualisation/parallels-guest.nix
+++ b/nixos/modules/virtualisation/parallels-guest.nix
@@ -3,9 +3,7 @@
 with lib;
 
 let
-
-  prl-tools = config.boot.kernelPackages.prl-tools;
-
+  prl-tools = config.hardware.parallels.package;
 in
 
 {
@@ -22,12 +20,31 @@ in
         '';
       };
 
+      autoMountShares = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Control prlfsmountd service. When this service is running, shares can not be manually
+          mounted through `mount -t prl_fs ...` as this service will remount and trample any set options.
+          Recommended to enable for simple file sharing, but extended share use such as for code should
+          disable this to manually mount shares.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = config.boot.kernelPackages.prl-tools;
+        defaultText = "config.boot.kernelPackages.prl-tools";
+        example = literalExample "config.boot.kernelPackages.prl-tools";
+        description = ''
+          Defines which package to use for prl-tools. Override to change the version.
+        '';
+      };
     };
 
   };
 
   config = mkIf config.hardware.parallels.enable {
-
     services.xserver = {
       drivers = singleton
         { name = "prlvideo"; modules = [ prl-tools ]; libPath = [ prl-tools ]; };
@@ -55,7 +72,7 @@ in
 
     boot.extraModulePackages = [ prl-tools ];
 
-    boot.kernelModules = [ "prl_tg" "prl_eth" "prl_fs" "prl_fs_freeze" "acpi_memhotplug" ];
+    boot.kernelModules = [ "prl_tg" "prl_eth" "prl_fs" "prl_fs_freeze" ];
 
     services.timesyncd.enable = false;
 
@@ -68,7 +85,7 @@ in
       };
     };
 
-    systemd.services.prlfsmountd = {
+    systemd.services.prlfsmountd = mkIf config.hardware.parallels.autoMountShares {
       description = "Parallels Shared Folders Daemon";
       wantedBy = [ "multi-user.target" ];
       serviceConfig = rec {
@@ -89,5 +106,50 @@ in
       };
     };
 
+    systemd.user.services = {
+      prlcc = {
+        description = "Parallels Control Center";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlcc";
+        };
+      };
+      prldnd = {
+        description = "Parallels Control Center";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prldnd";
+        };
+      };
+      prl_wmouse_d  = {
+        description = "Parallels Walking Mouse Daemon";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prl_wmouse_d";
+        };
+      };
+      prlcp = {
+        description = "Parallels CopyPaste Tool";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlcp";
+        };
+      };
+      prlsga = {
+        description = "Parallels Shared Guest Applications Tool";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlsga";
+        };
+      };
+      prlshprof = {
+        description = "Parallels Shared Profile Tool";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlshprof";
+        };
+      };
+    };
+
   };
 }
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 3c89ca68113b..45325c6b0d8d 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -10,6 +10,7 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with import ../../lib/qemu-flags.nix { inherit pkgs; };
 
 let
 
@@ -23,13 +24,13 @@ let
   cfg = config.virtualisation;
 
   qemuGraphics = if cfg.graphics then "" else "-nographic";
-  kernelConsole = if cfg.graphics then "" else "console=ttyS0";
+  kernelConsole = if cfg.graphics then "" else "console=${qemuSerialDevice}";
   ttys = [ "tty1" "tty2" "tty3" "tty4" "tty5" "tty6" ];
 
   # Shell script to start the VM.
   startVM =
     ''
-      #! ${pkgs.stdenv.shell}
+      #! ${pkgs.runtimeShell}
 
       NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}})
 
@@ -72,11 +73,10 @@ let
       '')}
 
       # Start QEMU.
-      exec ${qemu}/bin/qemu-kvm \
+      exec ${qemuBinary qemu} \
           -name ${vmName} \
           -m ${toString config.virtualisation.memorySize} \
           -smp ${toString config.virtualisation.cores} \
-          ${optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \
           ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
           -virtfs local,path=/nix/store,security_model=none,mount_tag=store \
           -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
@@ -98,7 +98,7 @@ let
           ${qemuGraphics} \
           ${toString config.virtualisation.qemu.options} \
           $QEMU_OPTS \
-          $@
+          "$@"
     '';
 
 
@@ -319,8 +319,8 @@ in
       networkingOptions =
         mkOption {
           default = [
-            "-net nic,vlan=0,model=virtio"
-            "-net user,vlan=0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
+            "-net nic,netdev=user.0,model=virtio"
+            "-netdev user,id=user.0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
           ];
           type = types.listOf types.str;
           description = ''
@@ -434,7 +434,11 @@ in
 
     virtualisation.pathsInNixDB = [ config.system.build.toplevel ];
 
-    virtualisation.qemu.options = [ "-vga std" "-usbdevice tablet" ];
+    # FIXME: Consolidate this one day.
+    virtualisation.qemu.options = mkMerge [
+      (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0" ])
+      (mkIf (pkgs.stdenv.isArm || pkgs.stdenv.isAarch64) [ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" ])
+    ];
 
     # Mount the host filesystem via 9P, and bind-mount the Nix store
     # of the host into our own filesystem.  We use mkVMOverride to
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index bb0c38bd4eb8..7413e12c8f3d 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -124,7 +124,7 @@ in
           '';
       };
 
-    networking.interfaces.vboxnet0.ip4 = [ { address = "192.168.56.1"; prefixLength = 24; } ];
+    networking.interfaces.vboxnet0.ipv4.addresses = [{ address = "192.168.56.1"; prefixLength = 24; }];
     # Make sure NetworkManager won't assume this interface being up
     # means we have internet access.
     networking.networkmanager.unmanaged = ["vboxnet0"];
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index d68b3bb73904..64f145f77ca3 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -8,8 +8,6 @@ let
 
 in {
 
-  imports = [ ./grow-partition.nix ];
-
   options = {
     virtualbox = {
       baseImageSize = mkOption {
@@ -23,12 +21,11 @@ in {
   };
 
   config = {
-
     system.build.virtualBoxOVA = import ../../lib/make-disk-image.nix {
-      name = "nixos-ova-${config.system.nixosLabel}-${pkgs.stdenv.system}";
+      name = "nixos-ova-${config.system.nixos.label}-${pkgs.stdenv.system}";
 
       inherit pkgs lib config;
-      partitioned = true;
+      partitionTableType = "legacy";
       diskSize = cfg.baseImageSize;
 
       postVM =
@@ -40,7 +37,7 @@ in {
           VBoxManage internalcommands createrawvmdk -filename disk.vmdk -rawdisk $diskImage
 
           echo "creating VirtualBox VM..."
-          vmName="NixOS ${config.system.nixosLabel} (${pkgs.stdenv.system})"
+          vmName="NixOS ${config.system.nixos.label} (${pkgs.stdenv.system})"
           VBoxManage createvm --name "$vmName" --register \
             --ostype ${if pkgs.stdenv.system == "x86_64-linux" then "Linux26_64" else "Linux26"}
           VBoxManage modifyvm "$vmName" \
@@ -56,7 +53,7 @@ in {
 
           echo "exporting VirtualBox VM..."
           mkdir -p $out
-          fn="$out/nixos-${config.system.nixosLabel}-${pkgs.stdenv.system}.ova"
+          fn="$out/nixos-${config.system.nixos.label}-${pkgs.stdenv.system}.ova"
           VBoxManage export "$vmName" --output "$fn"
 
           rm -v $diskImage
@@ -71,6 +68,7 @@ in {
       autoResize = true;
     };
 
+    boot.growPartition = true;
     boot.loader.grub.device = "/dev/sda";
 
     virtualisation.virtualbox.guest.enable = true;
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index c7656bc309c0..cf57868acef9 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -35,24 +35,19 @@ in
       description = ''
         The package used for Xen binary.
       '';
+      relatedPackages = [ "xen" "xen-light" ];
     };
 
-    virtualisation.xen.qemu = mkOption {
-      type = types.path;
-      defaultText = "\${pkgs.xen}/lib/xen/bin/qemu-system-i386";
-      example = literalExample "''${pkgs.qemu_xen-light}/bin/qemu-system-i386";
-      description = ''
-        The qemu binary to use for Dom-0 backend.
-      '';
-    };
-
-    virtualisation.xen.qemu-package = mkOption {
+    virtualisation.xen.package-qemu = mkOption {
       type = types.package;
       defaultText = "pkgs.xen";
       example = literalExample "pkgs.qemu_xen-light";
       description = ''
-        The package with qemu binaries for xendomains.
+        The package with qemu binaries for dom0 qemu and xendomains.
       '';
+      relatedPackages = [ "xen"
+                          { name = "qemu_xen-light"; comment = "For use with pkgs.xen-light."; }
+                        ];
     };
 
     virtualisation.xen.bootParams =
@@ -158,8 +153,7 @@ in
     } ];
 
     virtualisation.xen.package = mkDefault pkgs.xen;
-    virtualisation.xen.qemu = mkDefault "${pkgs.xen}/lib/xen/bin/qemu-system-i386";
-    virtualisation.xen.qemu-package = mkDefault pkgs.xen;
+    virtualisation.xen.package-qemu = mkDefault pkgs.xen;
     virtualisation.xen.stored = mkDefault "${cfg.package}/bin/oxenstored";
 
     environment.systemPackages = [ cfg.package ];
@@ -247,6 +241,12 @@ in
           '';
           target = "default/xendomains";
         }
+      ]
+      ++ lib.optionals (builtins.compareVersions cfg.package.version "4.10" >= 0) [
+        # in V 4.10 oxenstored requires /etc/xen/oxenstored.conf to start
+        { source = "${cfg.package}/etc/xen/oxenstored.conf";
+          target = "xen/oxenstored.conf";
+        }
       ];
 
     # Xen provides udev rules.
@@ -268,7 +268,7 @@ in
         mkdir -p /var/lib/xen # so we create them here unconditionally.
         grep -q control_d /proc/xen/capabilities
         '';
-      serviceConfig = if cfg.package.version < "4.8" then
+      serviceConfig = if (builtins.compareVersions cfg.package.version "4.8" < 0) then
         { ExecStart = ''
             ${cfg.stored}${optionalString cfg.trace " -T /var/log/xen/xenstored-trace.log"} --no-fork
             '';
@@ -281,7 +281,7 @@ in
           NotifyAccess    = "all";
         };
       postStart = ''
-        ${optionalString (cfg.package.version < "4.8") ''
+        ${optionalString (builtins.compareVersions cfg.package.version "4.8" < 0) ''
           time=0
           timeout=30
           # Wait for xenstored to actually come up, timing out after 30 seconds
@@ -326,7 +326,7 @@ in
       serviceConfig = {
         ExecStart = ''
           ${cfg.package}/bin/xenconsoled\
-            ${optionalString ((cfg.package.version >= "4.8")) " -i"}\
+            ${optionalString ((builtins.compareVersions cfg.package.version "4.8" >= 0)) " -i"}\
             ${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
           '';
       };
@@ -339,7 +339,8 @@ in
       after = [ "xen-console.service" ];
       requires = [ "xen-store.service" ];
       serviceConfig.ExecStart = ''
-        ${cfg.qemu} -xen-attach -xen-domid 0 -name dom0 -M xenpv \
+        ${cfg.package-qemu}/${cfg.package-qemu.qemu-system-i386} \
+           -xen-attach -xen-domid 0 -name dom0 -M xenpv \
            -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null
         '';
     };
@@ -448,7 +449,7 @@ in
       before = [ "dhcpd.service" ];
       restartIfChanged = false;
       serviceConfig.RemainAfterExit = "yes";
-      path = [ cfg.package cfg.qemu-package ];
+      path = [ cfg.package cfg.package-qemu ];
       environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains";
       preStart = "mkdir -p /var/lock/subsys -m 755";
       serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start";
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index 7536bf3e48ef..e010b532a688 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -2,7 +2,7 @@
 # and nixos-14.04). The channel is updated every time the ‘tested’ job
 # succeeds, and all other jobs have finished (they may fail).
 
-{ nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
+{ nixpkgs ? { outPath = (import ../lib).cleanSource ./..; revCount = 56789; shortRev = "gfedcba"; }
 , stableBranch ? false
 , supportedSystems ? [ "x86_64-linux" ]
 , limitedSupportedSystems ? [ "i686-linux" ]
@@ -52,16 +52,17 @@ in rec {
         (all nixos.dummy)
         (all nixos.manual)
 
-        (all nixos.iso_minimal)
-        nixos.iso_graphical.x86_64-linux
-        nixos.ova.x86_64-linux
+        nixos.iso_minimal.x86_64-linux or []
+        nixos.iso_minimal.i686-linux or []
+        nixos.iso_graphical.x86_64-linux or []
+        nixos.ova.x86_64-linux or []
 
         #(all nixos.tests.containers)
-        nixos.tests.chromium
+        nixos.tests.chromium.x86_64-linux or []
         (all nixos.tests.firefox)
         (all nixos.tests.firewall)
         (all nixos.tests.gnome3)
-        nixos.tests.installer.zfsroot.x86_64-linux # ZFS is 64bit only
+        nixos.tests.installer.zfsroot.x86_64-linux or [] # ZFS is 64bit only
         (all nixos.tests.installer.lvm)
         (all nixos.tests.installer.luksroot)
         (all nixos.tests.installer.separateBoot)
@@ -80,7 +81,7 @@ in rec {
         (all nixos.tests.boot.uefiUsb)
         (all nixos.tests.boot-stage1)
         (all nixos.tests.hibernate)
-        nixos.tests.docker
+        nixos.tests.docker.x86_64-linux or []
         (all nixos.tests.ecryptfs)
         (all nixos.tests.env)
         (all nixos.tests.ipv6)
@@ -95,6 +96,7 @@ in rec {
         #(all nixos.tests.lightdm)
         (all nixos.tests.login)
         (all nixos.tests.misc)
+        (all nixos.tests.mutableUsers)
         (all nixos.tests.nat.firewall)
         (all nixos.tests.nat.standalone)
         (all nixos.tests.networking.scripted.loopback)
@@ -115,6 +117,7 @@ in rec {
         (all nixos.tests.sddm.default)
         (all nixos.tests.simple)
         (all nixos.tests.slim)
+        (all nixos.tests.switchTest)
         (all nixos.tests.udisks2)
         (all nixos.tests.xfce)
 
diff --git a/nixos/release-small.nix b/nixos/release-small.nix
index e9f3cfb4de53..4bfb9a423f7d 100644
--- a/nixos/release-small.nix
+++ b/nixos/release-small.nix
@@ -2,7 +2,7 @@
 # small subset of Nixpkgs, mostly useful for servers that need fast
 # security updates.
 
-{ nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
+{ nixpkgs ? { outPath = (import ../lib).cleanSource ./..; revCount = 56789; shortRev = "gfedcba"; }
 , stableBranch ? false
 , supportedSystems ? [ "x86_64-linux" ] # no i686-linux
 }:
@@ -41,6 +41,7 @@ in rec {
         nfs3
         openssh
         php-pcre
+        predictable-interface-names
         proxy
         simple;
       installer = {
@@ -49,6 +50,10 @@ in rec {
           separateBoot
           simple;
       };
+      boot = {
+        inherit (nixos'.tests.boot)
+          biosCdrom;
+      };
     };
   };
 
diff --git a/nixos/release.nix b/nixos/release.nix
index 367fdadd0f45..7a12baba5c1c 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -1,8 +1,9 @@
-{ nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
+{ nixpkgs ? { outPath = (import ../lib).cleanSource ./..; revCount = 130979; shortRev = "gfedcba"; }
 , stableBranch ? false
-, supportedSystems ? [ "x86_64-linux" ]
+, supportedSystems ? [ "x86_64-linux" "aarch64-linux" ]
 }:
 
+with import ../pkgs/top-level/release-lib.nix { inherit supportedSystems; };
 with import ../lib;
 
 let
@@ -11,15 +12,19 @@ let
   versionSuffix =
     (if stableBranch then "." else "pre") + "${toString nixpkgs.revCount}.${nixpkgs.shortRev}";
 
-  forAllSystems = genAttrs supportedSystems;
-
   importTest = fn: args: system: import fn ({
     inherit system;
   } // args);
 
-  callTest = fn: args: forAllSystems (system: hydraJob (importTest fn args system));
+  # Note: only supportedSystems are considered.
+  callTestOnMatchingSystems = systems: fn: args:
+    forMatchingSystems
+      (intersectLists supportedSystems systems)
+      (system: hydraJob (importTest fn args system));
+  callTest = callTestOnMatchingSystems supportedSystems;
 
-  callSubTests = fn: args: let
+  callSubTests = callSubTestsOnMatchingSystems supportedSystems;
+  callSubTestsOnMatchingSystems = systems: fn: args: let
     discover = attrs: let
       subTests = filterAttrs (const (hasAttr "test")) attrs;
     in mapAttrs (const (t: hydraJob t.test)) subTests;
@@ -28,17 +33,14 @@ let
       ${system} = test;
     }) (discover (importTest fn args system));
 
-  # If the test is only for a particular system, use only the specified
-  # system instead of generating attributes for all available systems.
-  in if args ? system then discover (import fn args)
-     else foldAttrs mergeAttrs {} (map discoverForSystem supportedSystems);
+  in foldAttrs mergeAttrs {} (map discoverForSystem (intersectLists systems supportedSystems));
 
   pkgs = import nixpkgs { system = "x86_64-linux"; };
 
 
   versionModule =
-    { system.nixosVersionSuffix = versionSuffix;
-      system.nixosRevision = nixpkgs.rev or nixpkgs.shortRev;
+    { system.nixos.versionSuffix = versionSuffix;
+      system.nixos.revision = nixpkgs.rev or nixpkgs.shortRev;
     };
 
 
@@ -53,6 +55,17 @@ let
     }).config.system.build.isoImage);
 
 
+  makeSdImage =
+    { module, maintainers ? ["dezgeg"], system }:
+
+    with import nixpkgs { inherit system; };
+
+    hydraJob ((import lib/eval-config.nix {
+      inherit system;
+      modules = [ module versionModule ];
+    }).config.system.build.sdImage);
+
+
   makeSystemTarball =
     { module, maintainers ? ["viric"], system }:
 
@@ -89,6 +102,28 @@ let
       });
   }).config));
 
+  makeNetboot = config:
+    let
+      configEvaled = import lib/eval-config.nix config;
+      build = configEvaled.config.system.build;
+      kernelTarget = configEvaled.pkgs.stdenv.platform.kernelTarget;
+    in
+      pkgs.symlinkJoin {
+        name = "netboot";
+        paths = [
+          build.netbootRamdisk
+          build.kernel
+          build.netbootIpxeScript
+        ];
+        postBuild = ''
+          mkdir -p $out/nix-support
+          echo "file ${kernelTarget} $out/${kernelTarget}" >> $out/nix-support/hydra-build-products
+          echo "file initrd $out/initrd" >> $out/nix-support/hydra-build-products
+          echo "file ipxe $out/netboot.ipxe" >> $out/nix-support/hydra-build-products
+        '';
+        preferLocalBuild = true;
+      };
+
 
 in rec {
 
@@ -103,28 +138,13 @@ in rec {
   # Build the initial ramdisk so Hydra can keep track of its size over time.
   initialRamdisk = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.initialRamdisk);
 
-  netboot.x86_64-linux = let build = (import lib/eval-config.nix {
-      system = "x86_64-linux";
-      modules = [
-        ./modules/installer/netboot/netboot-minimal.nix
-        versionModule
-      ];
-    }).config.system.build;
-  in
-    pkgs.symlinkJoin {
-      name="netboot";
-      paths=[
-        build.netbootRamdisk
-        build.kernel
-        build.netbootIpxeScript
-      ];
-      postBuild = ''
-        mkdir -p $out/nix-support
-        echo "file bzImage $out/bzImage" >> $out/nix-support/hydra-build-products
-        echo "file initrd $out/initrd" >> $out/nix-support/hydra-build-products
-        echo "file ipxe $out/netboot.ipxe" >> $out/nix-support/hydra-build-products
-      '';
-    };
+  netboot = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: makeNetboot {
+    inherit system;
+    modules = [
+      ./modules/installer/netboot/netboot-minimal.nix
+      versionModule
+    ];
+  });
 
   iso_minimal = forAllSystems (system: makeIso {
     module = ./modules/installer/cd-dvd/installation-cd-minimal.nix;
@@ -132,7 +152,7 @@ in rec {
     inherit system;
   });
 
-  iso_graphical = genAttrs [ "x86_64-linux" ] (system: makeIso {
+  iso_graphical = forMatchingSystems [ "x86_64-linux" ] (system: makeIso {
     module = ./modules/installer/cd-dvd/installation-cd-graphical-kde.nix;
     type = "graphical";
     inherit system;
@@ -140,15 +160,23 @@ in rec {
 
   # A variant with a more recent (but possibly less stable) kernel
   # that might support more hardware.
-  iso_minimal_new_kernel = genAttrs [ "x86_64-linux" ] (system: makeIso {
+  iso_minimal_new_kernel = forMatchingSystems [ "x86_64-linux" ] (system: makeIso {
     module = ./modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix;
     type = "minimal-new-kernel";
     inherit system;
   });
 
+  sd_image = forMatchingSystems [ "armv6l-linux" "armv7l-linux" "aarch64-linux" ] (system: makeSdImage {
+    module = {
+        armv6l-linux = ./modules/installer/cd-dvd/sd-image-raspberrypi.nix;
+        armv7l-linux = ./modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix;
+        aarch64-linux = ./modules/installer/cd-dvd/sd-image-aarch64.nix;
+      }.${system};
+    inherit system;
+  });
 
   # A bootable VirtualBox virtual appliance as an OVA file (i.e. packaged OVF).
-  ova = genAttrs [ "x86_64-linux" ] (system:
+  ova = forMatchingSystems [ "x86_64-linux" ] (system:
 
     with import nixpkgs { inherit system; };
 
@@ -217,12 +245,16 @@ in rec {
   tests.atd = callTest tests/atd.nix {};
   tests.acme = callTest tests/acme.nix {};
   tests.avahi = callTest tests/avahi.nix {};
+  tests.beegfs = callTest tests/beegfs.nix {};
   tests.bittorrent = callTest tests/bittorrent.nix {};
   tests.blivet = callTest tests/blivet.nix {};
   tests.boot = callSubTests tests/boot.nix {};
   tests.boot-stage1 = callTest tests/boot-stage1.nix {};
-  tests.cadvisor = hydraJob (import tests/cadvisor.nix { system = "x86_64-linux"; });
-  tests.chromium = (callSubTests tests/chromium.nix { system = "x86_64-linux"; }).stable;
+  tests.borgbackup = callTest tests/borgbackup.nix {};
+  tests.buildbot = callTest tests/buildbot.nix {};
+  tests.cadvisor = callTestOnMatchingSystems ["x86_64-linux"] tests/cadvisor.nix {};
+  tests.ceph = callTestOnMatchingSystems ["x86_64-linux"] tests/ceph.nix {};
+  tests.chromium = (callSubTestsOnMatchingSystems ["x86_64-linux"] tests/chromium.nix {}).stable or {};
   tests.cjdns = callTest tests/cjdns.nix {};
   tests.cloud-init = callTest tests/cloud-init.nix {};
   tests.containers-ipv4 = callTest tests/containers-ipv4.nix {};
@@ -235,23 +267,27 @@ in rec {
   tests.containers-tmpfs = callTest tests/containers-tmpfs.nix {};
   tests.containers-hosts = callTest tests/containers-hosts.nix {};
   tests.containers-macvlans = callTest tests/containers-macvlans.nix {};
-  tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; });
-  tests.docker-edge = hydraJob (import tests/docker-edge.nix { system = "x86_64-linux"; });
+  tests.couchdb = callTest tests/couchdb.nix {};
+  tests.docker = callTestOnMatchingSystems ["x86_64-linux"] tests/docker.nix {};
+  tests.docker-tools = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-tools.nix {};
+  tests.docker-tools-overlay = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-tools-overlay.nix {};
+  tests.docker-edge = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-edge.nix {};
   tests.dovecot = callTest tests/dovecot.nix {};
-  tests.dnscrypt-proxy = callTest tests/dnscrypt-proxy.nix { system = "x86_64-linux"; };
+  tests.dnscrypt-proxy = callTestOnMatchingSystems ["x86_64-linux"] tests/dnscrypt-proxy.nix {};
   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;
-  tests.elk = hydraJob (import tests/elk.nix { system = "x86_64-linux"; });
+  tests.etcd = callTestOnMatchingSystems ["x86_64-linux"] tests/etcd.nix {};
+  tests.ec2-nixops = (callSubTestsOnMatchingSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-nixops or {};
+  tests.ec2-config = (callSubTestsOnMatchingSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-config or {};
+  tests.elk = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/elk.nix {};
   tests.env = callTest tests/env.nix {};
   tests.ferm = callTest tests/ferm.nix {};
   tests.firefox = callTest tests/firefox.nix {};
   tests.firewall = callTest tests/firewall.nix {};
-  tests.fleet = hydraJob (import tests/fleet.nix { system = "x86_64-linux"; });
+  tests.fleet = callTestOnMatchingSystems ["x86_64-linux"] tests/fleet.nix {};
+  tests.fwupd = callTest tests/fwupd.nix {};
   #tests.gitlab = callTest tests/gitlab.nix {};
   tests.gitolite = callTest tests/gitolite.nix {};
-  tests.glance = callTest tests/glance.nix {};
+  tests.gjs = callTest tests/gjs.nix {};
   tests.gocd-agent = callTest tests/gocd-agent.nix {};
   tests.gocd-server = callTest tests/gocd-server.nix {};
   tests.gnome3 = callTest tests/gnome3.nix {};
@@ -260,14 +296,18 @@ in rec {
   tests.graphite = callTest tests/graphite.nix {};
   tests.hardened = callTest tests/hardened.nix { };
   tests.hibernate = callTest tests/hibernate.nix {};
+  tests.home-assistant = callTest tests/home-assistant.nix { };
   tests.hound = callTest tests/hound.nix {};
+  tests.hocker-fetchdocker = callTest tests/hocker-fetchdocker {};
   tests.i3wm = callTest tests/i3wm.nix {};
-  tests.initrd-network-ssh = callTest tests/initrd-network-ssh.nix {};
+  tests.iftop = callTest tests/iftop.nix {};
+  tests.initrd-network-ssh = callTest tests/initrd-network-ssh {};
   tests.installer = callSubTests tests/installer.nix {};
   tests.influxdb = callTest tests/influxdb.nix {};
   tests.ipv6 = callTest tests/ipv6.nix {};
   tests.jenkins = callTest tests/jenkins.nix {};
   tests.plasma5 = callTest tests/plasma5.nix {};
+  tests.plotinus = callTest tests/plotinus.nix {};
   tests.keymap = callSubTests tests/keymap.nix {};
   tests.initrdNetwork = callTest tests/initrd-network.nix {};
   tests.kafka_0_9 = callTest tests/kafka_0_9.nix {};
@@ -277,25 +317,30 @@ in rec {
   tests.kernel-copperhead = callTest tests/kernel-copperhead.nix {};
   tests.kernel-latest = callTest tests/kernel-latest.nix {};
   tests.kernel-lts = callTest tests/kernel-lts.nix {};
-  tests.keystone = callTest tests/keystone.nix {};
-  tests.kubernetes = hydraJob (import tests/kubernetes/default.nix { system = "x86_64-linux"; });
+  tests.kubernetes.dns = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/kubernetes/dns.nix {};
+  ## kubernetes.e2e should eventually replace kubernetes.rbac when it works
+  #tests.kubernetes.e2e = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/kubernetes/e2e.nix {};
+  tests.kubernetes.rbac = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/kubernetes/rbac.nix {};
   tests.latestKernel.login = callTest tests/login.nix { latestKernel = true; };
   tests.ldap = callTest tests/ldap.nix {};
   #tests.lightdm = callTest tests/lightdm.nix {};
   tests.login = callTest tests/login.nix {};
   #tests.logstash = callTest tests/logstash.nix {};
   tests.mathics = callTest tests/mathics.nix {};
+  tests.matrix-synapse = callTest tests/matrix-synapse.nix {};
   tests.mesos = callTest tests/mesos.nix {};
   tests.misc = callTest tests/misc.nix {};
   tests.mongodb = callTest tests/mongodb.nix {};
   tests.mumble = callTest tests/mumble.nix {};
   tests.munin = callTest tests/munin.nix {};
+  tests.mutableUsers = callTest tests/mutable-users.nix {};
   tests.mysql = callTest tests/mysql.nix {};
   tests.mysqlBackup = callTest tests/mysql-backup.nix {};
   tests.mysqlReplication = callTest tests/mysql-replication.nix {};
   tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; };
   tests.nat.firewall-conntrack = callTest tests/nat.nix { withFirewall = true; withConntrackHelpers = true; };
   tests.nat.standalone = callTest tests/nat.nix { withFirewall = false; };
+  tests.netdata = callTest tests/netdata.nix { };
   tests.networking.networkd = callSubTests tests/networking.nix { networkd = true; };
   tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; };
   # TODO: put in networking.nix after the test becomes more complete
@@ -304,38 +349,56 @@ in rec {
   tests.nfs4 = callTest tests/nfs.nix { version = 4; };
   tests.nginx = callTest tests/nginx.nix { };
   tests.nghttpx = callTest tests/nghttpx.nix { };
+  tests.nix-ssh-serve = callTest tests/nix-ssh-serve.nix { };
+  tests.novacomd = callTestOnMatchingSystems ["x86_64-linux"] tests/novacomd.nix { };
   tests.leaps = callTest tests/leaps.nix { };
   tests.nsd = callTest tests/nsd.nix {};
   tests.openssh = callTest tests/openssh.nix {};
+  tests.openldap = callTest tests/openldap.nix {};
+  tests.owncloud = callTest tests/owncloud.nix {};
   tests.pam-oath-login = callTest tests/pam-oath-login.nix {};
-  #tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
+  #tests.panamax = callTestOnMatchingSystems ["x86_64-linux"] tests/panamax.nix {};
   tests.peerflix = callTest tests/peerflix.nix {};
   tests.php-pcre = callTest tests/php-pcre.nix {};
   tests.postgresql = callSubTests tests/postgresql.nix {};
   tests.pgmanage = callTest tests/pgmanage.nix {};
   tests.postgis = callTest tests/postgis.nix {};
+  tests.powerdns = callTest tests/powerdns.nix {};
   #tests.pgjwt = callTest tests/pgjwt.nix {};
+  tests.predictable-interface-names = callSubTests tests/predictable-interface-names.nix {};
   tests.printing = callTest tests/printing.nix {};
   tests.prometheus = callTest tests/prometheus.nix {};
+  tests.prosody = callTest tests/prosody.nix {};
   tests.proxy = callTest tests/proxy.nix {};
-  tests.pumpio = callTest tests/pump.io.nix {};
-  # tests.quagga = callTest tests/quagga.nix {};
+  tests.quagga = callTest tests/quagga.nix {};
   tests.quake3 = callTest tests/quake3.nix {};
+  tests.rabbitmq = callTest tests/rabbitmq.nix {};
   tests.radicale = callTest tests/radicale.nix {};
+  tests.rspamd = callSubTests tests/rspamd.nix {};
   tests.runInMachine = callTest tests/run-in-machine.nix {};
+  tests.rxe = callTest tests/rxe.nix {};
   tests.samba = callTest tests/samba.nix {};
   tests.sddm = callSubTests tests/sddm.nix {};
   tests.simple = callTest tests/simple.nix {};
   tests.slim = callTest tests/slim.nix {};
   tests.smokeping = callTest tests/smokeping.nix {};
   tests.snapper = callTest tests/snapper.nix {};
+  tests.statsd = callTest tests/statsd.nix {};
+  tests.sudo = callTest tests/sudo.nix {};
+  tests.systemd = callTest tests/systemd.nix {};
+  tests.switchTest = callTest tests/switch-test.nix {};
   tests.taskserver = callTest tests/taskserver.nix {};
   tests.tomcat = callTest tests/tomcat.nix {};
+  tests.transmission = callTest tests/transmission.nix {};
   tests.udisks2 = callTest tests/udisks2.nix {};
-  tests.virtualbox = callSubTests tests/virtualbox.nix { system = "x86_64-linux"; };
+  tests.vault = callTest tests/vault.nix {};
+  tests.virtualbox = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/virtualbox.nix {};
   tests.wordpress = callTest tests/wordpress.nix {};
+  tests.xautolock = callTest tests/xautolock.nix {};
   tests.xfce = callTest tests/xfce.nix {};
   tests.xmonad = callTest tests/xmonad.nix {};
+  tests.xrdp = callTest tests/xrdp.nix {};
+  tests.yabar = callTest tests/yabar.nix {};
   tests.zookeeper = callTest tests/zookeeper.nix {};
 
   /* Build a bunch of typical closures so that Hydra can keep track of
diff --git a/nixos/tests/atd.nix b/nixos/tests/atd.nix
index c2c0a716e0de..5260c8ddfb82 100644
--- a/nixos/tests/atd.nix
+++ b/nixos/tests/atd.nix
@@ -17,20 +17,14 @@ import ./make-test.nix ({ pkgs, lib, ... }:
     startAll;
 
     $machine->fail("test -f ~root/at-1");
-    $machine->fail("test -f ~root/batch-1");
     $machine->fail("test -f ~alice/at-1");
-    $machine->fail("test -f ~alice/batch-1");
 
     $machine->succeed("echo 'touch ~root/at-1' | at now+1min");
-    $machine->succeed("echo 'touch ~root/batch-1' | batch");
     $machine->succeed("su - alice -c \"echo 'touch at-1' | at now+1min\"");
-    $machine->succeed("su - alice -c \"echo 'touch batch-1' | batch\"");
 
     $machine->succeed("sleep 1.5m");
 
     $machine->succeed("test -f ~root/at-1");
-    $machine->succeed("test -f ~root/batch-1");
     $machine->succeed("test -f ~alice/at-1");
-    $machine->succeed("test -f ~alice/batch-1");
   '';
 })
diff --git a/nixos/tests/beegfs.nix b/nixos/tests/beegfs.nix
new file mode 100644
index 000000000000..433910feafe3
--- /dev/null
+++ b/nixos/tests/beegfs.nix
@@ -0,0 +1,115 @@
+import ./make-test.nix ({ pkgs, ... } :
+
+let
+  connAuthFile="beegfs/auth-def.key";
+
+  client = { config, pkgs, lib, ... } : {
+    networking.firewall.enable = false;
+    services.beegfsEnable = true;
+    services.beegfs.default = {
+      mgmtdHost = "mgmt";
+      connAuthFile = "/etc/${connAuthFile}";
+      client = {
+        mount = false;
+        enable = true;
+      };
+    };
+
+    fileSystems = pkgs.lib.mkVMOverride # FIXME: this should be creatd by the module
+      [ { mountPoint = "/beegfs";
+          device = "default";
+          fsType = "beegfs";
+          options = [ "cfgFile=/etc/beegfs/client-default.conf" "_netdev" ];
+        }
+      ];
+
+    environment.etc."${connAuthFile}" = {
+      enable = true;
+      text = "ThisIsALousySecret";
+      mode = "0600";
+    };
+  };
+
+
+  server = service : { config, pkgs, lib, ... } : {
+    networking.firewall.enable = false;
+    boot.initrd.postDeviceCommands = ''
+      ${pkgs.e2fsprogs}/bin/mkfs.ext4 -L data /dev/vdb
+    '';
+
+    virtualisation.emptyDiskImages = [ 4096 ];
+
+    fileSystems = pkgs.lib.mkVMOverride
+      [ { mountPoint = "/data";
+          device = "/dev/disk/by-label/data";
+          fsType = "ext4";
+        }
+      ];
+
+    environment.systemPackages = with pkgs; [ beegfs ];
+    environment.etc."${connAuthFile}" = {
+      enable = true;
+      text = "ThisIsALousySecret";
+      mode = "0600";
+    };
+
+    services.beegfsEnable = true;
+    services.beegfs.default = {
+      mgmtdHost = "mgmt";
+      connAuthFile = "/etc/${connAuthFile}";
+      "${service}" = {
+        enable = true;
+        storeDir = "/data";
+      };
+    };
+  };
+
+in
+{
+  name = "beegfs";
+
+  nodes = {
+    meta = server "meta";
+    mgmt = server "mgmtd";
+    storage1 = server "storage";
+    storage2 = server "storage";
+    client1 = client;
+    client2 = client;
+  };
+
+  testScript = ''
+    # Initalize the data directories
+    $mgmt->waitForUnit("default.target");
+    $mgmt->succeed("beegfs-setup-mgmtd -C -f -p /data");
+    $mgmt->succeed("systemctl start beegfs-mgmtd-default");
+
+    $meta->waitForUnit("default.target");
+    $meta->succeed("beegfs-setup-meta -C -f -s 1 -p /data");
+    $meta->succeed("systemctl start beegfs-meta-default");
+
+    $storage1->waitForUnit("default.target");
+    $storage1->succeed("beegfs-setup-storage -C -f -s 1 -i 1 -p /data");
+    $storage1->succeed("systemctl start beegfs-storage-default");
+
+    $storage2->waitForUnit("default.target");
+    $storage2->succeed("beegfs-setup-storage -C -f -s 2 -i 2 -p /data");
+    $storage2->succeed("systemctl start beegfs-storage-default");
+
+    #
+
+    # Basic test
+    $client1->waitForUnit("beegfs.mount");
+    $client1->succeed("beegfs-check-servers-default");
+    $client1->succeed("echo test > /beegfs/test");
+    $client2->waitForUnit("beegfs.mount");
+    $client2->succeed("test -e /beegfs/test");
+    $client2->succeed("cat /beegfs/test | grep test");
+
+    # test raid0/stripping
+    $client1->succeed("dd if=/dev/urandom bs=1M count=10 of=/beegfs/striped");
+    $client2->succeed("cat /beegfs/striped > /dev/null");
+
+    # check if fs is still healthy
+    $client1->succeed("beegfs-fsck-default --checkfs");
+  '';
+})
diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix
index 3a718a798315..50c98664660a 100644
--- a/nixos/tests/bittorrent.nix
+++ b/nixos/tests/bittorrent.nix
@@ -16,7 +16,7 @@ let
   miniupnpdConf = nodes: pkgs.writeText "miniupnpd.conf"
     ''
       ext_ifname=eth1
-      listening_ip=${(pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ip4).address}/24
+      listening_ip=${(pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address}/24
       allow 1024-65535 192.168.2.0/24 1024-65535
     '';
 
@@ -56,7 +56,7 @@ in
         { environment.systemPackages = [ pkgs.transmission ];
           virtualisation.vlans = [ 2 ];
           networking.defaultGateway =
-            (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ip4).address;
+            (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address;
           networking.firewall.enable = false;
         };
 
@@ -84,7 +84,7 @@ in
       # Create the torrent.
       $tracker->succeed("mkdir /tmp/data");
       $tracker->succeed("cp ${file} /tmp/data/test.tar.bz2");
-      $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 -p -t http://${(pkgs.lib.head nodes.tracker.config.networking.interfaces.eth1.ip4).address}:6969/announce -o /tmp/test.torrent");
+      $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 -p -t http://${(pkgs.lib.head nodes.tracker.config.networking.interfaces.eth1.ipv4.addresses).address}:6969/announce -o /tmp/test.torrent");
       $tracker->succeed("chmod 644 /tmp/test.torrent");
 
       # Start the tracker.  !!! use a less crappy tracker
diff --git a/nixos/tests/boot-stage1.nix b/nixos/tests/boot-stage1.nix
index eeaca9f50edc..b2e74bff6fcd 100644
--- a/nixos/tests/boot-stage1.nix
+++ b/nixos/tests/boot-stage1.nix
@@ -9,6 +9,7 @@ import ./make-test.nix ({ pkgs, ... }: {
         kver = config.boot.kernelPackages.kernel.modDirVersion;
         ksrc = "${kdev}/lib/modules/${kver}/build";
         hardeningDisable = [ "pic" ];
+        nativeBuildInputs = kdev.moduleBuildDependencies;
       } ''
         echo "obj-m += $name.o" > Makefile
         echo "$source" > "$name.c"
diff --git a/nixos/tests/boot.nix b/nixos/tests/boot.nix
index 69ab4755e444..301d9d0f817f 100644
--- a/nixos/tests/boot.nix
+++ b/nixos/tests/boot.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
@@ -25,6 +24,12 @@ let
           my $machine = createMachine({ ${machineConfig}, qemuFlags => '-m 768' });
           $machine->start;
           $machine->waitForUnit("multi-user.target");
+          $machine->succeed("nix verify -r --no-trust /run/current-system");
+
+          # Test whether the channel got installed correctly.
+          $machine->succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello");
+          $machine->succeed("nix-env --dry-run -iA nixos.procps");
+
           $machine->shutdown;
         '';
     };
diff --git a/nixos/tests/borgbackup.nix b/nixos/tests/borgbackup.nix
new file mode 100644
index 000000000000..36731773de27
--- /dev/null
+++ b/nixos/tests/borgbackup.nix
@@ -0,0 +1,162 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+let
+  passphrase = "supersecret";
+  dataDir = "/ran:dom/data";
+  excludeFile = "not_this_file";
+  keepFile = "important_file";
+  keepFileData = "important_data";
+  localRepo = "/root/back:up";
+  archiveName = "my_archive";
+  remoteRepo = "borg@server:."; # No need to specify path
+  privateKey = pkgs.writeText "id_ed25519" ''
+    -----BEGIN OPENSSH PRIVATE KEY-----
+    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+    QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe
+    RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw
+    AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg
+    9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ=
+    -----END OPENSSH PRIVATE KEY-----
+  '';
+  publicKey = ''
+    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv root@client
+  '';
+  privateKeyAppendOnly = pkgs.writeText "id_ed25519" ''
+    -----BEGIN OPENSSH PRIVATE KEY-----
+    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+    QyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLwAAAJC9YTxxvWE8
+    cQAAAAtzc2gtZWQyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLw
+    AAAEAAhV7wTl5dL/lz+PF/d4PnZXuG1Id6L/mFEiGT1tZsuFpxm7PUQsZB2Ejs8Xp0YVp8
+    IOW+HylIRzhweORbRCMvAAAADXJzY2h1ZXR6QGt1cnQ=
+    -----END OPENSSH PRIVATE KEY-----
+  '';
+  publicKeyAppendOnly = ''
+    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpxm7PUQsZB2Ejs8Xp0YVp8IOW+HylIRzhweORbRCMv root@client
+  '';
+
+in {
+  name = "borgbackup";
+  meta = with pkgs.stdenv.lib; {
+    maintainers = with maintainers; [ dotlambda ];
+  };
+
+  nodes = {
+    client = { config, pkgs, ... }: {
+      services.borgbackup.jobs = {
+        
+        local = rec {
+          paths = dataDir;
+          repo = localRepo;
+          preHook = ''
+            # Don't append a timestamp
+            archiveName="${archiveName}"
+          '';
+          encryption = {
+            mode = "repokey";
+            inherit passphrase;
+          };
+          compression = "auto,zlib,9";
+          prune.keep = {
+            within = "1y";
+            yearly = 5;
+          };
+          exclude = [ "*/${excludeFile}" ];
+          postHook = "echo post";
+          startAt = [ ]; # Do not run automatically
+        };
+
+        remote = {
+          paths = dataDir;
+          repo = remoteRepo;
+          encryption.mode = "none";
+          startAt = [ ];
+          environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
+        };
+
+        remoteAppendOnly = {
+          paths = dataDir;
+          repo = remoteRepo;
+          encryption.mode = "none";
+          startAt = [ ];
+          environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly";
+        };
+
+      };
+    };
+
+    server = { config, pkgs, ... }: {
+      services.openssh = {
+        enable = true;
+        passwordAuthentication = false;
+        challengeResponseAuthentication = false;
+      };
+
+      services.borgbackup.repos.repo1 = {
+        authorizedKeys = [ publicKey ];
+        path = "/data/borgbackup";
+      };
+
+      # Second repo to make sure the authorizedKeys options are merged correctly
+      services.borgbackup.repos.repo2 = {
+        authorizedKeysAppendOnly = [ publicKeyAppendOnly ];
+        path = "/data/borgbackup";
+        quota = ".5G";
+      };
+    };
+  };
+
+  testScript = ''
+    startAll;
+
+    $client->fail('test -d "${remoteRepo}"');
+
+    $client->succeed("cp ${privateKey} /root/id_ed25519");
+    $client->succeed("chmod 0600 /root/id_ed25519");
+    $client->succeed("cp ${privateKeyAppendOnly} /root/id_ed25519.appendOnly");
+    $client->succeed("chmod 0600 /root/id_ed25519.appendOnly");
+
+    $client->succeed("mkdir -p ${dataDir}");
+    $client->succeed("touch ${dataDir}/${excludeFile}");
+    $client->succeed("echo '${keepFileData}' > ${dataDir}/${keepFile}");
+
+    subtest "local", sub {
+      my $borg = "BORG_PASSPHRASE='${passphrase}' borg";
+      $client->systemctl("start --wait borgbackup-job-local");
+      $client->fail("systemctl is-failed borgbackup-job-local");
+      # Make sure exactly one archive has been created
+      $client->succeed("c=\$($borg list '${localRepo}' | wc -l) && [[ \$c == '1' ]]");
+      # Make sure excludeFile has been excluded
+      $client->fail("$borg list '${localRepo}::${archiveName}' | grep -qF '${excludeFile}'");
+      # Make sure keepFile has the correct content
+      $client->succeed("$borg extract '${localRepo}::${archiveName}'");
+      $client->succeed('c=$(cat ${dataDir}/${keepFile}) && [[ "$c" == "${keepFileData}" ]]');
+    };
+
+    subtest "remote", sub {
+      my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519' borg";
+      $server->waitForUnit("sshd.service");
+      $client->waitForUnit("network.target");
+      $client->systemctl("start --wait borgbackup-job-remote");
+      $client->fail("systemctl is-failed borgbackup-job-remote");
+
+      # Make sure we can't access repos other than the specified one
+      $client->fail("$borg list borg\@server:wrong");
+
+      #TODO: Make sure that data is actually deleted
+    };
+
+    subtest "remoteAppendOnly", sub {
+      my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly' borg";
+      $server->waitForUnit("sshd.service");
+      $client->waitForUnit("network.target");
+      $client->systemctl("start --wait borgbackup-job-remoteAppendOnly");
+      $client->fail("systemctl is-failed borgbackup-job-remoteAppendOnly");
+
+      # Make sure we can't access repos other than the specified one
+      $client->fail("$borg list borg\@server:wrong");
+
+      #TODO: Make sure that data is not actually deleted
+    };
+
+  '';
+})
diff --git a/nixos/tests/ceph.nix b/nixos/tests/ceph.nix
new file mode 100644
index 000000000000..b9993062c079
--- /dev/null
+++ b/nixos/tests/ceph.nix
@@ -0,0 +1,140 @@
+import ./make-test.nix ({pkgs, ...}: rec {
+  name = "All-in-one-basic-ceph-cluster";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lejonet ];
+  };
+
+  nodes = {
+    aio = { config, pkgs, ... }: {
+      virtualisation = {
+        emptyDiskImages = [ 20480 20480 ];
+        vlans = [ 1 ];
+      };
+      
+      networking = {
+        firewall.allowPing = true;
+        useDHCP = false;
+        interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
+          { address = "192.168.1.1"; prefixLength = 24; }
+        ];
+      };
+
+      environment.systemPackages = with pkgs; [
+        bash
+        sudo
+        ceph
+        xfsprogs
+      ];
+      nixpkgs.config.packageOverrides = super: {
+        ceph = super.ceph.override({ nss = super.nss; libxfs = super.libxfs; libaio = super.libaio; jemalloc = super.jemalloc; });
+      };
+
+      boot.kernelModules = [ "xfs" ];
+
+      services.ceph.enable = true;
+      services.ceph.global = {
+        fsid = "066ae264-2a5d-4729-8001-6ad265f50b03";
+        monInitialMembers = "aio";
+        monHost = "192.168.1.1";
+      };
+
+      services.ceph.mon = {
+        enable = true;
+        daemons = [ "aio" ];
+      };
+
+      services.ceph.mgr = {
+        enable = true;
+        daemons = [ "aio" ];
+      };
+
+      services.ceph.osd = {
+        enable = true;
+        daemons = [ "0" "1" ];
+      };
+    };
+  };
+  
+  testScript = { nodes, ... }: ''
+    startAll;
+
+    $aio->waitForUnit("network.target");
+
+    # Create the ceph-related directories
+    $aio->mustSucceed(
+      "mkdir -p /var/lib/ceph/mgr/ceph-aio/",
+      "mkdir -p /var/lib/ceph/mon/ceph-aio/",
+      "mkdir -p /var/lib/ceph/osd/ceph-{0..1}/",
+      "chown ceph:ceph -R /var/lib/ceph/"
+    );
+
+    # Bootstrap ceph-mon daemon
+    $aio->mustSucceed(
+      "mkdir -p /var/lib/ceph/bootstrap-osd && chown ceph:ceph /var/lib/ceph/bootstrap-osd",
+      "sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
+      "ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --set-uid=0 --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
+      "ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
+            "monmaptool --create --add aio 192.168.1.1 --fsid 066ae264-2a5d-4729-8001-6ad265f50b03 /tmp/monmap",
+      "sudo -u ceph ceph-mon --mkfs -i aio --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
+      "touch /var/lib/ceph/mon/ceph-aio/done",
+      "systemctl start ceph-mon-aio"
+    );
+    $aio->waitForUnit("ceph-mon-aio");
+
+    # Can't check ceph status until a mon is up
+    $aio->succeed("ceph -s | grep 'mon: 1 daemons'");
+          
+    # Start the ceph-mgr daemon, it has no deps and hardly any setup
+    $aio->mustSucceed(
+      "ceph auth get-or-create mgr.aio mon 'allow profile mgr' osd 'allow *' mds 'allow *' > /var/lib/ceph/mgr/ceph-aio/keyring",
+      "systemctl start ceph-mgr-aio"
+    );
+    $aio->waitForUnit("ceph-mgr-aio");
+    $aio->waitUntilSucceeds("ceph -s | grep 'quorum aio'");
+
+    # Bootstrap both OSDs
+    $aio->mustSucceed(
+      "mkfs.xfs /dev/vdb",
+      "mkfs.xfs /dev/vdc",
+      "mount /dev/vdb /var/lib/ceph/osd/ceph-0",
+      "mount /dev/vdc /var/lib/ceph/osd/ceph-1",
+      "ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-0/keyring --name osd.0 --add-key AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==",
+      "ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-1/keyring --name osd.1 --add-key AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==",
+      "echo '{\"cephx_secret\": \"AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==\"}' | ceph osd new 55ba2294-3e24-478f-bee0-9dca4c231dd9 -i -",
+      "echo '{\"cephx_secret\": \"AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==\"}' | ceph osd new 5e97a838-85b6-43b0-8950-cb56d554d1e5 -i -"
+    );
+
+    # Initialize the OSDs with regular filestore
+    $aio->mustSucceed(
+      "ceph-osd -i 0 --mkfs --osd-uuid 55ba2294-3e24-478f-bee0-9dca4c231dd9",
+      "ceph-osd -i 1 --mkfs --osd-uuid 5e97a838-85b6-43b0-8950-cb56d554d1e5",
+      "chown -R ceph:ceph /var/lib/ceph/osd",
+      "systemctl start ceph-osd-0",
+      "systemctl start ceph-osd-1"
+    );
+
+    $aio->waitUntilSucceeds("ceph osd stat | grep '2 osds: 2 up, 2 in'");
+    $aio->waitUntilSucceeds("ceph -s | grep 'mgr: aio(active)'");
+    $aio->waitUntilSucceeds("ceph -s | grep 'HEALTH_OK'");
+
+    $aio->mustSucceed(
+      "ceph osd pool create aio-test 100 100",
+      "ceph osd pool ls | grep 'aio-test'",
+      "ceph osd pool rename aio-test aio-other-test",
+      "ceph osd pool ls | grep 'aio-other-test'",
+      "ceph -s | grep '1 pools, 100 pgs'",
+      "ceph osd getcrushmap -o crush",
+      "crushtool -d crush -o decrushed",
+      "sed 's/step chooseleaf firstn 0 type host/step chooseleaf firstn 0 type osd/' decrushed > modcrush",
+      "crushtool -c modcrush -o recrushed",
+      "ceph osd setcrushmap -i recrushed",
+      "ceph osd pool set aio-other-test size 2"
+    );
+    $aio->waitUntilSucceeds("ceph -s | grep 'HEALTH_OK'");
+    $aio->waitUntilSucceeds("ceph -s | grep '100 active+clean'");
+    $aio->mustFail(
+      "ceph osd pool ls | grep 'aio-test'",
+      "ceph osd pool delete aio-other-test aio-other-test --yes-i-really-really-mean-it"
+    );
+  '';
+})
diff --git a/nixos/tests/chromium.nix b/nixos/tests/chromium.nix
index 3a2c65164766..65c314e22e1d 100644
--- a/nixos/tests/chromium.nix
+++ b/nixos/tests/chromium.nix
@@ -151,11 +151,11 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
 
       $machine->screenshot("sandbox_info");
 
-      $machine->succeed(ru "${xdo "submit-url" ''
+      $machine->succeed(ru "${xdo "find-window" ''
         search --sync --onlyvisible --name "sandbox status"
         windowfocus --sync
       ''}");
-      $machine->succeed(ru "${xdo "submit-url" ''
+      $machine->succeed(ru "${xdo "copy-sandbox-info" ''
         key --delay 1000 Ctrl+a Ctrl+c
       ''}");
 
@@ -166,6 +166,26 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
           && $clipboard =~ /network namespaces.*yes/mi
           && $clipboard =~ /seccomp.*sandbox.*yes/mi
           && $clipboard =~ /you are adequately sandboxed/mi;
+
+      $machine->sleep(1);
+      $machine->succeed(ru "${xdo "find-window-after-copy" ''
+        search --onlyvisible --name "sandbox status"
+      ''}");
+
+      my $clipboard = $machine->succeed(ru "echo void | ${pkgs.xclip}/bin/xclip -i");
+      $machine->succeed(ru "${xdo "copy-sandbox-info" ''
+        key --delay 1000 Ctrl+a Ctrl+c
+      ''}");
+
+      my $clipboard = $machine->succeed(ru "${pkgs.xclip}/bin/xclip -o");
+      die "copying twice in a row does not work properly: $clipboard"
+      unless $clipboard =~ /namespace sandbox.*yes/mi
+          && $clipboard =~ /pid namespaces.*yes/mi
+          && $clipboard =~ /network namespaces.*yes/mi
+          && $clipboard =~ /seccomp.*sandbox.*yes/mi
+          && $clipboard =~ /you are adequately sandboxed/mi;
+
+      $machine->screenshot("afer_copy_from_chromium");
     };
 
     $machine->shutdown;
diff --git a/nixos/tests/cjdns.nix b/nixos/tests/cjdns.nix
index 466663799241..4d3b58abc6e5 100644
--- a/nixos/tests/cjdns.nix
+++ b/nixos/tests/cjdns.nix
@@ -12,7 +12,6 @@ let
       # the sequence of address assignment less stochastic.
       networking.useDHCP = false;
 
-      networking.interfaces.eth1.prefixLength = 24;
       # CJDNS output is incompatible with the XML log.
       systemd.services.cjdns.serviceConfig.StandardOutput = "null";
       #networking.firewall.enable = true;
@@ -49,7 +48,9 @@ import ./make-test.nix ({ pkgs, ...} : {
 
           { imports = [ basicConfig ];
 
-          networking.interfaces.eth1.ipAddress = "192.168.0.2";
+          networking.interfaces.eth1.ipv4.addresses = [
+            { address = "192.168.0.2"; prefixLength = 24; }
+          ];
 
           services.cjdns =
             { UDPInterface =
@@ -76,7 +77,9 @@ import ./make-test.nix ({ pkgs, ...} : {
             CJDNS_ADMIN_PASSWORD=FOOBAR
           '';
 
-          networking.interfaces.eth1.ipAddress = "192.168.0.1";
+          networking.interfaces.eth1.ipv4.addresses = [
+            { address = "192.168.0.1"; prefixLength = 24; }
+          ];
 
           services.cjdns =
             { authorizedPasswords = [ carolPassword ];
diff --git a/nixos/tests/cloud-init.nix b/nixos/tests/cloud-init.nix
index c0add7eff360..2a258e4bff54 100644
--- a/nixos/tests/cloud-init.nix
+++ b/nixos/tests/cloud-init.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
diff --git a/nixos/tests/common/letsencrypt.nix b/nixos/tests/common/letsencrypt.nix
index 9b53d9d61a16..10cde45d18a8 100644
--- a/nixos/tests/common/letsencrypt.nix
+++ b/nixos/tests/common/letsencrypt.nix
@@ -138,8 +138,8 @@ let
   boulder = let
     owner = "letsencrypt";
     repo = "boulder";
-    rev = "9866abab8962a591f06db457a4b84c518cc88243";
-    version = "20170510";
+    rev = "9c6a1f2adc4c26d925588f5ae366cfd4efb7813a";
+    version = "20180129";
 
   in pkgs.buildGoPackage rec {
     name = "${repo}-${version}";
@@ -147,7 +147,7 @@ let
     src = pkgs.fetchFromGitHub {
       name = "${name}-src";
       inherit rev owner repo;
-      sha256 = "170m5cjngbrm36wi7wschqw8jzs7kxpcyzmshq3pcrmcpigrhna1";
+      sha256 = "09kszswrifm9rc6idfaq0p1mz5w21as2qbc8gd5pphrq9cf9pn55";
     };
 
     postPatch = ''
@@ -168,6 +168,18 @@ let
       cat "${snakeOilCa}/ca.pem" > test/test-ca.pem
     '';
 
+    # Until vendored pkcs11 is go 1.9 compatible
+    preBuild = ''
+      rm -r go/src/github.com/letsencrypt/boulder/vendor/github.com/miekg/pkcs11
+    '';
+
+    extraSrcs = map mkGoDep [
+      { goPackagePath = "github.com/miekg/pkcs11";
+        rev           = "6dbd569b952ec150d1425722dbbe80f2c6193f83";
+        sha256        = "1m8g6fx7df6hf6q6zsbyw1icjmm52dmsx28rgb0h930wagvngfwb";
+      }
+    ];
+
     goPackagePath = "github.com/${owner}/${repo}";
     buildInputs = [ pkgs.libtool ];
   };
@@ -284,7 +296,11 @@ let
     ocsp-updater.after = [ "boulder-publisher" ];
     ocsp-responder.args = "--config ${cfgDir}/ocsp-responder.json";
     ct-test-srv = {};
-    mail-test-srv.args = "--closeFirst 5";
+    mail-test-srv.args = let
+      key = "${boulderSource}/test/mail-test-srv/minica-key.pem";
+      crt = "${boulderSource}/test/mail-test-srv/minica.pem";
+     in
+      "--closeFirst 5 --cert ${crt} --key ${key}";
   };
 
   commonPath = [ softhsm pkgs.mariadb goose boulder ];
diff --git a/nixos/tests/containers-bridge.nix b/nixos/tests/containers-bridge.nix
index b8d4759684cc..dfef46a2ada4 100644
--- a/nixos/tests/containers-bridge.nix
+++ b/nixos/tests/containers-bridge.nix
@@ -26,8 +26,8 @@ import ./make-test.nix ({ pkgs, ...} : {
       };
       networking.interfaces = {
         br0 = {
-          ip4 = [{ address = hostIp; prefixLength = 24; }];
-          ip6 = [{ address = hostIp6; prefixLength = 7; }];
+          ipv4.addresses = [{ address = hostIp; prefixLength = 24; }];
+          ipv6.addresses = [{ address = hostIp6; prefixLength = 7; }];
         };
       };
 
diff --git a/nixos/tests/containers-extra_veth.nix b/nixos/tests/containers-extra_veth.nix
index 6339c8c558b9..df3f3354b2d9 100644
--- a/nixos/tests/containers-extra_veth.nix
+++ b/nixos/tests/containers-extra_veth.nix
@@ -21,11 +21,11 @@ import ./make-test.nix ({ pkgs, ...} : {
       };
       networking.interfaces = {
         br0 = {
-          ip4 = [{ address = "192.168.0.1"; prefixLength = 24; }];
-          ip6 = [{ address = "fc00::1"; prefixLength = 7; }];
+          ipv4.addresses = [{ address = "192.168.0.1"; prefixLength = 24; }];
+          ipv6.addresses = [{ address = "fc00::1"; prefixLength = 7; }];
         };
         br1 = {
-          ip4 = [{ address = "192.168.1.1"; prefixLength = 24; }];
+          ipv4.addresses = [{ address = "192.168.1.1"; prefixLength = 24; }];
         };
       };
 
diff --git a/nixos/tests/containers-hosts.nix b/nixos/tests/containers-hosts.nix
index c7a85f190a5d..df1ef6d14936 100644
--- a/nixos/tests/containers-hosts.nix
+++ b/nixos/tests/containers-hosts.nix
@@ -13,9 +13,9 @@ import ./make-test.nix ({ pkgs, ...} : {
       virtualisation.vlans = [];
 
       networking.bridges.br0.interfaces = [];
-      networking.interfaces.br0 = {
-        ip4 = [ { address = "10.11.0.254"; prefixLength = 24; } ];
-      };
+      networking.interfaces.br0.ipv4.addresses = [
+        { address = "10.11.0.254"; prefixLength = 24; }
+      ];
 
       # Force /etc/hosts to be the only source for host name resolution
       environment.etc."nsswitch.conf".text = lib.mkForce ''
diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix
index 7e2a54976387..015b79b1cee6 100644
--- a/nixos/tests/containers-imperative.nix
+++ b/nixos/tests/containers-imperative.nix
@@ -15,12 +15,12 @@ import ./make-test.nix ({ pkgs, ...} : {
       # container available within the VM, because we don't have network access.
       virtualisation.pathsInNixDB = let
         emptyContainer = import ../lib/eval-config.nix {
-          inherit (config.nixpkgs) system;
+          inherit (config.nixpkgs.localSystem) system;
           modules = lib.singleton {
             containers.foo.config = {};
           };
         };
-      in [ pkgs.stdenv emptyContainer.config.containers.foo.path ];
+      in [ pkgs.stdenv emptyContainer.config.containers.foo.path pkgs.libxslt ];
     };
 
   testScript =
diff --git a/nixos/tests/containers-macvlans.nix b/nixos/tests/containers-macvlans.nix
index 721f98481497..390dc4ad2c29 100644
--- a/nixos/tests/containers-macvlans.nix
+++ b/nixos/tests/containers-macvlans.nix
@@ -26,9 +26,9 @@ import ./make-test.nix ({ pkgs, ...} : {
           interface = "eth1";
           mode = "bridge";
         };
-        networking.interfaces.eth1.ip4 = lib.mkForce [];
+        networking.interfaces.eth1.ipv4.addresses = lib.mkForce [];
         networking.interfaces.mv-eth1-host = {
-          ip4 = [ { address = "192.168.1.1"; prefixLength = 24; } ];
+          ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
         };
 
         containers.test1 = {
@@ -37,7 +37,7 @@ import ./make-test.nix ({ pkgs, ...} : {
 
           config = {
             networking.interfaces.mv-eth1 = {
-              ip4 = [ { address = containerIp1; prefixLength = 24; } ];
+              ipv4.addresses = [ { address = containerIp1; prefixLength = 24; } ];
             };
           };
         };
@@ -48,7 +48,7 @@ import ./make-test.nix ({ pkgs, ...} : {
 
           config = {
             networking.interfaces.mv-eth1 = {
-              ip4 = [ { address = containerIp2; prefixLength = 24; } ];
+              ipv4.addresses = [ { address = containerIp2; prefixLength = 24; } ];
             };
           };
         };
diff --git a/nixos/tests/containers-physical_interfaces.nix b/nixos/tests/containers-physical_interfaces.nix
index a3b0b29951bf..bde8e175f953 100644
--- a/nixos/tests/containers-physical_interfaces.nix
+++ b/nixos/tests/containers-physical_interfaces.nix
@@ -16,9 +16,9 @@ import ./make-test.nix ({ pkgs, ...} : {
           interfaces = [ "eth1" ];
 
           config = {
-            networking.interfaces.eth1 = {
-              ip4 = [ { address = "10.10.0.1"; prefixLength = 24; } ];
-            };
+            networking.interfaces.eth1.ipv4.addresses = [
+              { address = "10.10.0.1"; prefixLength = 24; }
+            ];
             networking.firewall.enable = false;
           };
         };
@@ -33,9 +33,9 @@ import ./make-test.nix ({ pkgs, ...} : {
 
         config = {
           networking.bridges.br0.interfaces = [ "eth1" ];
-          networking.interfaces.br0 = {
-            ip4 = [ { address = "10.10.0.2"; prefixLength = 24; } ];
-          };
+          networking.interfaces.br0.ipv4.addresses = [
+            { address = "10.10.0.2"; prefixLength = 24; }
+          ];
           networking.firewall.enable = false;
         };
       };
@@ -52,11 +52,11 @@ import ./make-test.nix ({ pkgs, ...} : {
         config = {
           networking.bonds.bond0 = {
             interfaces = [ "eth1" ];
-            mode = "active-backup";
-          };
-          networking.interfaces.bond0 = {
-            ip4 = [ { address = "10.10.0.3"; prefixLength = 24; } ];
+            driverOptions.mode = "active-backup";
           };
+          networking.interfaces.bond0.ipv4.addresses = [
+            { address = "10.10.0.3"; prefixLength = 24; }
+          ];
           networking.firewall.enable = false;
         };
       };
@@ -73,12 +73,12 @@ import ./make-test.nix ({ pkgs, ...} : {
         config = {
           networking.bonds.bond0 = {
             interfaces = [ "eth1" ];
-            mode = "active-backup";
+            driverOptions.mode = "active-backup";
           };
           networking.bridges.br0.interfaces = [ "bond0" ];
-          networking.interfaces.br0 = {
-            ip4 = [ { address = "10.10.0.4"; prefixLength = 24; } ];
-          };
+          networking.interfaces.br0.ipv4.addresses = [
+            { address = "10.10.0.4"; prefixLength = 24; }
+          ];
           networking.firewall.enable = false;
         };
       };
diff --git a/nixos/tests/containers-reloadable.nix b/nixos/tests/containers-reloadable.nix
index b5867c6f6ab1..5fb42f2272b3 100644
--- a/nixos/tests/containers-reloadable.nix
+++ b/nixos/tests/containers-reloadable.nix
@@ -11,7 +11,7 @@ let
 
     # prevent make-test.nix to change IP
     networking.interfaces = {
-      eth1.ip4 = lib.mkOverride 0 [ ];
+      eth1.ipv4.addresses = lib.mkOverride 0 [ ];
     };
   };
 in {
diff --git a/nixos/tests/containers-restart_networking.nix b/nixos/tests/containers-restart_networking.nix
index 086d056c51cd..f68c9b07759b 100644
--- a/nixos/tests/containers-restart_networking.nix
+++ b/nixos/tests/containers-restart_networking.nix
@@ -11,7 +11,7 @@ let
       config = {
         networking.firewall.enable = false;
         networking.firewall.allowPing = true;
-        networking.interfaces.eth0.ip4 = [
+        networking.interfaces.eth0.ipv4.addresses = [
           { address = "192.168.1.122"; prefixLength = 24; }
         ];
       };
@@ -33,8 +33,8 @@ in import ./make-test.nix ({ pkgs, lib, ...} :
         rstp = false;
       };
       networking.interfaces = {
-        eth1.ip4 = lib.mkOverride 0 [ ];
-        br0.ip4 = [{ address = "192.168.1.1"; prefixLength = 24; }];
+        eth1.ipv4.addresses = lib.mkOverride 0 [ ];
+        br0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
       };
 
     };
@@ -44,8 +44,8 @@ in import ./make-test.nix ({ pkgs, lib, ...} :
         rstp = false;
       };
       networking.interfaces = {
-        eth1.ip4 = lib.mkOverride 0 [ ];
-        br0.ip4 = [{ address = "192.168.1.2"; prefixLength = 24; }];
+        eth1.ipv4.addresses = lib.mkOverride 0 [ ];
+        br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
       };
     };
     client_eth1_rstp = { lib, pkgs, ... }: client_base // {
@@ -54,8 +54,8 @@ in import ./make-test.nix ({ pkgs, lib, ...} :
         rstp = true;
       };
       networking.interfaces = {
-        eth1.ip4 = lib.mkOverride 0 [ ];
-        br0.ip4 = [{ address = "192.168.1.2"; prefixLength = 24; }];
+        eth1.ipv4.addresses = lib.mkOverride 0 [ ];
+        br0.ipv4.addresses =  [ { address = "192.168.1.2"; prefixLength = 24; } ];
       };
     };
   };
diff --git a/nixos/tests/containers-tmpfs.nix b/nixos/tests/containers-tmpfs.nix
index 564831fa2737..873dd364369f 100644
--- a/nixos/tests/containers-tmpfs.nix
+++ b/nixos/tests/containers-tmpfs.nix
@@ -1,7 +1,7 @@
 # Test for NixOS' container support.
 
 import ./make-test.nix ({ pkgs, ...} : {
-  name = "containers-bridge";
+  name = "containers-tmpfs";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ ckampka ];
   };
diff --git a/nixos/tests/couchdb.nix b/nixos/tests/couchdb.nix
new file mode 100644
index 000000000000..a3f675236bc6
--- /dev/null
+++ b/nixos/tests/couchdb.nix
@@ -0,0 +1,56 @@
+import ./make-test.nix ({ pkgs, lib, ...}:
+
+with lib;
+
+{
+  name = "couchdb";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ fpletz ];
+  };
+
+  nodes = {
+    couchdb1 =
+      { pkgs, config, ... }:
+
+      { environment.systemPackages = with pkgs; [ jq ];
+        services.couchdb.enable = true;
+      };
+
+    couchdb2 =
+      { pkgs, config, ... }:
+
+      { environment.systemPackages = with pkgs; [ jq ];
+        services.couchdb.enable = true;
+        services.couchdb.package = pkgs.couchdb2;
+      };
+  };
+
+  testScript = let
+    curlJqCheck = action: path: jqexpr: result:
+      pkgs.writeScript "curl-jq-check-${action}-${path}.sh" ''
+        RESULT=$(curl -X ${action} http://127.0.0.1:5984/${path} | jq -r '${jqexpr}')
+        echo $RESULT >&2
+        if [ "$RESULT" != "${result}" ]; then
+          exit 1
+        fi
+      '';
+  in ''
+    startAll;
+
+    $couchdb1->waitForUnit("couchdb.service");
+    $couchdb1->waitUntilSucceeds("${curlJqCheck "GET" "" ".couchdb" "Welcome"}");
+    $couchdb1->waitUntilSucceeds("${curlJqCheck "GET" "_all_dbs" ". | length" "2"}");
+    $couchdb1->succeed("${curlJqCheck "PUT" "foo" ".ok" "true"}");
+    $couchdb1->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "3"}");
+    $couchdb1->succeed("${curlJqCheck "DELETE" "foo" ".ok" "true"}");
+    $couchdb1->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "2"}");
+
+    $couchdb2->waitForUnit("couchdb.service");
+    $couchdb2->waitUntilSucceeds("${curlJqCheck "GET" "" ".couchdb" "Welcome"}");
+    $couchdb2->waitUntilSucceeds("${curlJqCheck "GET" "_all_dbs" ". | length" "0"}");
+    $couchdb2->succeed("${curlJqCheck "PUT" "foo" ".ok" "true"}");
+    $couchdb2->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "1"}");
+    $couchdb2->succeed("${curlJqCheck "DELETE" "foo" ".ok" "true"}");
+    $couchdb2->succeed("${curlJqCheck "GET" "_all_dbs" ". | length" "0"}");
+  '';
+})
diff --git a/nixos/tests/docker-tools-overlay.nix b/nixos/tests/docker-tools-overlay.nix
new file mode 100644
index 000000000000..9d7fa3e7a8c5
--- /dev/null
+++ b/nixos/tests/docker-tools-overlay.nix
@@ -0,0 +1,32 @@
+# this test creates a simple GNU image with docker tools and sees if it executes
+
+import ./make-test.nix ({ pkgs, ... }:
+{
+  name = "docker-tools-overlay";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lnl7 ];
+  };
+
+  nodes = {
+    docker =
+      { config, pkgs, ... }:
+      {
+        virtualisation.docker.enable = true;
+        virtualisation.docker.storageDriver = "overlay";  # defaults to overlay2
+      };
+  };
+
+  testScript =
+    ''
+      $docker->waitForUnit("sockets.target");
+
+      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'");
+      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.bash.imageName} bash --version");
+
+      # Check if the nix store has correct user permissions depending on what
+      # storage driver is used, incorrectly built images can show up as readonly.
+      # drw-------  3 0 0   3 Apr 14 11:36 /nix
+      # drw------- 99 0 0 100 Apr 14 11:36 /nix/store
+      $docker->succeed("docker run --rm -u 1000:1000 ${pkgs.dockerTools.examples.bash.imageName} bash --version");
+    '';
+})
diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix
new file mode 100644
index 000000000000..4466081d01e9
--- /dev/null
+++ b/nixos/tests/docker-tools.nix
@@ -0,0 +1,49 @@
+# this test creates a simple GNU image with docker tools and sees if it executes
+
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "docker-tools";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lnl7 ];
+  };
+
+  nodes = {
+    docker =
+      { config, pkgs, ... }: {
+        virtualisation = {
+          diskSize = 2048;
+          docker.enable = true;
+        };
+      };
+  };
+
+  testScript =
+    ''
+      $docker->waitForUnit("sockets.target");
+
+      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'");
+      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.bash.imageName} bash --version");
+      $docker->succeed("docker rmi ${pkgs.dockerTools.examples.bash.imageName}");
+
+      # Check if the nix store is correctly initialized by listing dependencies of the installed Nix binary
+      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nix}'");
+      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.nix.imageName} nix-store -qR ${pkgs.nix}");
+      $docker->succeed("docker rmi ${pkgs.dockerTools.examples.nix.imageName}");
+
+      # To test the pullImage tool
+      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nixFromDockerHub}'");
+      $docker->succeed("docker run --rm nixos/nix:1.11 nix-store --version");
+      $docker->succeed("docker rmi nixos/nix:1.11");
+
+      # To test runAsRoot and entry point
+      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nginx}'");
+      $docker->succeed("docker run --name nginx -d -p 8000:80 ${pkgs.dockerTools.examples.nginx.imageName}");
+      $docker->waitUntilSucceeds('curl http://localhost:8000/');
+      $docker->succeed("docker rm --force nginx");
+      $docker->succeed("docker rmi '${pkgs.dockerTools.examples.nginx.imageName}'");
+
+      # An pulled image can be used as base image
+      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.onTopOfPulledImage}'");
+      $docker->succeed("docker run --rm ontopofpulledimage hello");
+      $docker->succeed("docker rmi ontopofpulledimage");
+    '';
+})
diff --git a/nixos/tests/dovecot.nix b/nixos/tests/dovecot.nix
index 3814855ed8e7..156079d1d585 100644
--- a/nixos/tests/dovecot.nix
+++ b/nixos/tests/dovecot.nix
@@ -18,6 +18,18 @@ import ./make-test.nix {
         MAIL
       '';
 
+      sendTestMailViaDeliveryAgent = pkgs.writeScriptBin "send-lda" ''
+        #!${pkgs.stdenv.shell}
+
+        exec ${pkgs.dovecot}/libexec/dovecot/deliver -d bob <<MAIL
+        From: root@localhost
+        To: bob@localhost
+        Subject: Something else...
+
+        I'm running short of ideas!
+        MAIL
+      '';
+
       testImap = pkgs.writeScriptBin "test-imap" ''
         #!${pkgs.python3.interpreter}
         import imaplib
@@ -39,24 +51,25 @@ import ./make-test.nix {
 
         pop = poplib.POP3('localhost')
         try:
-          pop.user('alice')
+          pop.user('bob')
           pop.pass_('foobar')
           assert len(pop.list()[1]) == 1
           status, fullmail, size = pop.retr(1)
           assert status.startswith(b'+OK ')
           body = b"".join(fullmail[fullmail.index(b""):]).strip()
-          assert body == b'Hello world!'
+          assert body == b"I'm running short of ideas!"
         finally:
           pop.quit()
       '';
 
-    in [ sendTestMail testImap testPop ];
+    in [ sendTestMail sendTestMailViaDeliveryAgent testImap testPop ];
   };
 
   testScript = ''
     $machine->waitForUnit('postfix.service');
     $machine->waitForUnit('dovecot2.service');
     $machine->succeed('send-testmail');
+    $machine->succeed('send-lda');
     $machine->waitUntilFails('[ "$(postqueue -p)" != "Mail queue is empty" ]');
     $machine->succeed('test-imap');
     $machine->succeed('test-pop');
diff --git a/nixos/tests/ec2.nix b/nixos/tests/ec2.nix
index 4ec7e56cc6cb..f585fa2ec237 100644
--- a/nixos/tests/ec2.nix
+++ b/nixos/tests/ec2.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix
index 65ff1cac070b..ed656b3628b9 100644
--- a/nixos/tests/elk.nix
+++ b/nixos/tests/elk.nix
@@ -1,95 +1,107 @@
-# Test the ELK stack: Elasticsearch, Logstash and Kibana.
-
-import ./make-test.nix ({ pkgs, ...} :
+{ system ? builtins.currentSystem }:
+with import ../lib/testing.nix { inherit system; };
+with pkgs.lib;
 let
   esUrl = "http://localhost:9200";
-in {
-  name = "ELK";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ eelco chaoflow offline basvandijk ];
-  };
 
-  nodes = {
-    one =
-      { config, pkgs, ... }: {
-          # Not giving the machine at least 2060MB results in elasticsearch failing with the following error:
-          #
-          #   OpenJDK 64-Bit Server VM warning:
-          #     INFO: os::commit_memory(0x0000000085330000, 2060255232, 0)
-          #     failed; error='Cannot allocate memory' (errno=12)
-          #
-          #   There is insufficient memory for the Java Runtime Environment to continue.
-          #   Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory.
-          #
-          # When setting this to 2500 I got "Kernel panic - not syncing: Out of
-          # memory: compulsory panic_on_oom is enabled" so lets give it even a
-          # bit more room:
-          virtualisation.memorySize = 3000;
+  mkElkTest = name : elk : makeTest {
+    inherit name;
+    meta = with pkgs.stdenv.lib.maintainers; {
+      maintainers = [ eelco chaoflow offline basvandijk ];
+    };
+    nodes = {
+      one =
+        { config, pkgs, ... }: {
+            # Not giving the machine at least 2060MB results in elasticsearch failing with the following error:
+            #
+            #   OpenJDK 64-Bit Server VM warning:
+            #     INFO: os::commit_memory(0x0000000085330000, 2060255232, 0)
+            #     failed; error='Cannot allocate memory' (errno=12)
+            #
+            #   There is insufficient memory for the Java Runtime Environment to continue.
+            #   Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory.
+            #
+            # When setting this to 2500 I got "Kernel panic - not syncing: Out of
+            # memory: compulsory panic_on_oom is enabled" so lets give it even a
+            # bit more room:
+            virtualisation.memorySize = 3000;
 
-          # For querying JSON objects returned from elasticsearch and kibana.
-          environment.systemPackages = [ pkgs.jq ];
+            # For querying JSON objects returned from elasticsearch and kibana.
+            environment.systemPackages = [ pkgs.jq ];
 
-          services = {
-            logstash = {
-              enable = true;
-              package = pkgs.logstash5;
-              inputConfig = ''
-                exec { command => "echo -n flowers" interval => 1 type => "test" }
-                exec { command => "echo -n dragons" interval => 1 type => "test" }
-              '';
-              filterConfig = ''
-                if [message] =~ /dragons/ {
-                  drop {}
-                }
-              '';
-              outputConfig = ''
-                file {
-                  path => "/tmp/logstash.out"
-                  codec => line { format => "%{message}" }
-                }
-                elasticsearch {
-                  hosts => [ "${esUrl}" ]
-                }
-              '';
-            };
+            services = {
+              logstash = {
+                enable = true;
+                package = elk.logstash;
+                inputConfig = ''
+                  exec { command => "echo -n flowers" interval => 1 type => "test" }
+                  exec { command => "echo -n dragons" interval => 1 type => "test" }
+                '';
+                filterConfig = ''
+                  if [message] =~ /dragons/ {
+                    drop {}
+                  }
+                '';
+                outputConfig = ''
+                  file {
+                    path => "/tmp/logstash.out"
+                    codec => line { format => "%{message}" }
+                  }
+                  elasticsearch {
+                    hosts => [ "${esUrl}" ]
+                  }
+                '';
+              };
 
-            elasticsearch = {
-              enable = true;
-              package = pkgs.elasticsearch5;
-            };
+              elasticsearch = {
+                enable = true;
+                package = elk.elasticsearch;
+              };
 
-            kibana = {
-              enable = true;
-              package = pkgs.kibana5;
-              elasticsearch.url = esUrl;
+              kibana = {
+                enable = true;
+                package = elk.kibana;
+                elasticsearch.url = esUrl;
+              };
             };
           };
-        };
-    };
+      };
 
-  testScript = ''
-    startAll;
+    testScript = ''
+      startAll;
 
-    $one->waitForUnit("elasticsearch.service");
+      $one->waitForUnit("elasticsearch.service");
 
-    # Continue as long as the status is not "red". The status is probably
-    # "yellow" instead of "green" because we are using a single elasticsearch
-    # node which elasticsearch considers risky.
-    #
-    # TODO: extend this test with multiple elasticsearch nodes and see if the status turns "green".
-    $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_cluster/health' | jq .status | grep -v red");
+      # Continue as long as the status is not "red". The status is probably
+      # "yellow" instead of "green" because we are using a single elasticsearch
+      # node which elasticsearch considers risky.
+      #
+      # TODO: extend this test with multiple elasticsearch nodes and see if the status turns "green".
+      $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_cluster/health' | jq .status | grep -v red");
 
-    # Perform some simple logstash tests.
-    $one->waitForUnit("logstash.service");
-    $one->waitUntilSucceeds("cat /tmp/logstash.out | grep flowers");
-    $one->waitUntilSucceeds("cat /tmp/logstash.out | grep -v dragons");
+      # Perform some simple logstash tests.
+      $one->waitForUnit("logstash.service");
+      $one->waitUntilSucceeds("cat /tmp/logstash.out | grep flowers");
+      $one->waitUntilSucceeds("cat /tmp/logstash.out | grep -v dragons");
 
-    # See if kibana is healthy.
-    $one->waitForUnit("kibana.service");
-    $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green");
+      # See if kibana is healthy.
+      $one->waitForUnit("kibana.service");
+      $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green");
 
-    # See if logstash messages arive in elasticsearch.
-    $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0");
-    $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0");
-  '';
-})
+      # See if logstash messages arive in elasticsearch.
+      $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0");
+      $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0");
+    '';
+  };
+in mapAttrs mkElkTest {
+  "ELK-5" = {
+    elasticsearch = pkgs.elasticsearch5;
+    logstash      = pkgs.logstash5;
+    kibana        = pkgs.kibana5;
+  };
+  "ELK-6" = {
+    elasticsearch = pkgs.elasticsearch6;
+    logstash      = pkgs.logstash6;
+    kibana        = pkgs.kibana6;
+  };
+}
diff --git a/nixos/tests/ferm.nix b/nixos/tests/ferm.nix
index 8f2a8c01eebc..bb7daae118c0 100644
--- a/nixos/tests/ferm.nix
+++ b/nixos/tests/ferm.nix
@@ -11,8 +11,8 @@ import ./make-test.nix ({ pkgs, ...} : {
         with pkgs.lib;
         {
           networking = {
-            interfaces.eth1.ip6 = mkOverride 0 [ { address = "fd00::2"; prefixLength = 64; } ];
-            interfaces.eth1.ip4 = mkOverride 0 [ { address = "192.168.1.2"; prefixLength = 24; } ];
+            interfaces.eth1.ipv6.addresses = mkOverride 0 [ { address = "fd00::2"; prefixLength = 64; } ];
+            interfaces.eth1.ipv4.addresses = mkOverride 0 [ { address = "192.168.1.2"; prefixLength = 24; } ];
           };
       };
       server =
@@ -20,8 +20,8 @@ import ./make-test.nix ({ pkgs, ...} : {
         with pkgs.lib;
         {
           networking = {
-            interfaces.eth1.ip6 = mkOverride 0 [ { address = "fd00::1"; prefixLength = 64; } ];
-            interfaces.eth1.ip4 = mkOverride 0 [ { address = "192.168.1.1"; prefixLength = 24; } ];
+            interfaces.eth1.ipv6.addresses = mkOverride 0 [ { address = "fd00::1"; prefixLength = 64; } ];
+            interfaces.eth1.ipv4.addresses = mkOverride 0 [ { address = "192.168.1.1"; prefixLength = 24; } ];
           };
 
           services = {
diff --git a/nixos/tests/fwupd.nix b/nixos/tests/fwupd.nix
new file mode 100644
index 000000000000..bf4ef25130b3
--- /dev/null
+++ b/nixos/tests/fwupd.nix
@@ -0,0 +1,19 @@
+# run installed tests
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "fwupd";
+
+  meta = {
+    maintainers = pkgs.fwupd.meta.maintainers;
+  };
+
+  machine = { config, pkgs, ... }: {
+    services.fwupd.enable = true;
+    environment.systemPackages = with pkgs; [ gnome-desktop-testing ];
+    environment.variables.XDG_DATA_DIRS = [ "${pkgs.fwupd.installedTests}/share" ];
+    virtualisation.memorySize = 768;
+  };
+
+  testScript = ''
+    $machine->succeed("gnome-desktop-testing-runner");
+  '';
+})
diff --git a/nixos/tests/gjs.nix b/nixos/tests/gjs.nix
new file mode 100644
index 000000000000..e6002ef98dd0
--- /dev/null
+++ b/nixos/tests/gjs.nix
@@ -0,0 +1,19 @@
+# run installed tests
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "gjs";
+
+  meta = {
+    maintainers = pkgs.gnome3.gjs.meta.maintainers;
+  };
+
+  machine = { pkgs, ... }: {
+    imports = [ ./common/x11.nix ];
+    environment.systemPackages = with pkgs; [ gnome-desktop-testing ];
+    environment.variables.XDG_DATA_DIRS = [ "${pkgs.gnome3.gjs.installedTests}/share" ];
+  };
+
+  testScript = ''
+    $machine->waitForX;
+    $machine->succeed("gnome-desktop-testing-runner");
+  '';
+})
diff --git a/nixos/tests/glance.nix b/nixos/tests/glance.nix
deleted file mode 100644
index 992b77227a4b..000000000000
--- a/nixos/tests/glance.nix
+++ /dev/null
@@ -1,77 +0,0 @@
-{ system ? builtins.currentSystem }:
-
-with import ../lib/testing.nix { inherit system; };
-with pkgs.lib;
-
-let
-  glanceMysqlPassword = "glanceMysqlPassword";
-  glanceAdminPassword = "glanceAdminPassword";
-
-  createDb = pkgs.writeText "db-provisionning.sql" ''
-    create database keystone;
-    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'keystone';
-    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'keystone';
-
-    create database glance;
-    GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'localhost' IDENTIFIED BY '${glanceMysqlPassword}';
-    GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%' IDENTIFIED BY '${glanceMysqlPassword}';
-  '';
-
-  image =
-    (import ../lib/eval-config.nix {
-      inherit system;
-      modules = [ ../../nixos/modules/virtualisation/nova-image.nix ];
-    }).config.system.build.novaImage;
-
-  # The admin keystone account
-  adminOpenstackCmd = "OS_TENANT_NAME=admin OS_USERNAME=admin OS_PASSWORD=keystone OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
-
-in makeTest {
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ lewo ];
-  };
-  machine =
-    { config, pkgs, ... }:
-    {
-      services.mysql.enable = true;
-      services.mysql.package = pkgs.mysql;
-      services.mysql.initialScript = createDb;
-
-      virtualisation = {
-        openstack.keystone = {
-          enable = true;
-          database.password = { value = "keystone"; storage = "fromNixStore"; };
-          adminToken = { value = "adminToken"; storage = "fromNixStore"; };
-          bootstrap.enable = true;
-          bootstrap.adminPassword = { value = "keystone"; storage = "fromNixStore"; };
-        };
-
-        openstack.glance = {
-          enable = true;
-          database.password = { value = glanceMysqlPassword; storage = "fromNixStore"; };
-          servicePassword = { value = glanceAdminPassword; storage = "fromNixStore"; };
-
-          bootstrap = {
-            enable = true;
-            keystoneAdminPassword = { value = "keystone"; storage = "fromNixStore"; };
-          };
-        };
-
-        memorySize = 2096;
-        diskSize = 4 * 1024;
-        };
-
-      environment.systemPackages = with pkgs.pythonPackages; with pkgs; [
-        openstackclient
-      ];
-    };
-
-  testScript =
-    ''
-     $machine->waitForUnit("glance-api.service");
-
-     # Since Glance api can take time to start, we retry until success
-     $machine->waitUntilSucceeds("${adminOpenstackCmd} image create nixos --file ${image}/nixos.img --disk-format qcow2 --container-format bare --public");
-     $machine->succeed("${adminOpenstackCmd} image list") =~ /nixos/ or die;
-    '';
-}
diff --git a/nixos/tests/gnome3-gdm.nix b/nixos/tests/gnome3-gdm.nix
index 4b459e93e1be..71ae1709d526 100644
--- a/nixos/tests/gnome3-gdm.nix
+++ b/nixos/tests/gnome3-gdm.nix
@@ -26,15 +26,22 @@ import ./make-test.nix ({ pkgs, ...} : {
 
   testScript =
     ''
+      # wait for gdm to start and bring up X
+      $machine->waitForUnit("display-manager.service");
       $machine->waitForX;
-      $machine->sleep(15);
+
+      # wait for alice to be logged in
+      $machine->waitForUnit("default.target","alice");
 
       # Check that logging in has given the user ownership of devices.
       $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
 
-      $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
-      $machine->succeed("xauth merge ~alice/.Xauthority");
+      # open a terminal and check it's there
+      $machine->succeed("su - alice -c 'DISPLAY=:0.0 XAUTHORITY=/run/user/\$UID/gdm/Xauthority gnome-terminal'");
+      $machine->succeed("xauth merge /run/user/1000/gdm/Xauthority");
       $machine->waitForWindow(qr/Terminal/);
+
+      # wait to get a nice screenshot
       $machine->sleep(20);
       $machine->screenshot("screen");
     '';
diff --git a/nixos/tests/grafana.nix b/nixos/tests/grafana.nix
index 16b8181498a6..d45776c3ee29 100644
--- a/nixos/tests/grafana.nix
+++ b/nixos/tests/grafana.nix
@@ -20,6 +20,6 @@ import ./make-test.nix ({ lib, ... }:
     $machine->start;
     $machine->waitForUnit("grafana.service");
     $machine->waitForOpenPort(3000);
-    $machine->succeed("curl -sS http://127.0.0.1:3000/");
+    $machine->succeed("curl -sSfL http://127.0.0.1:3000/");
   '';
 })
diff --git a/nixos/tests/hocker-fetchdocker/default.nix b/nixos/tests/hocker-fetchdocker/default.nix
new file mode 100644
index 000000000000..4f30f01e4032
--- /dev/null
+++ b/nixos/tests/hocker-fetchdocker/default.nix
@@ -0,0 +1,15 @@
+import ../make-test.nix ({ pkgs, ...} : {
+  name = "test-hocker-fetchdocker";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ ixmatus ];
+  };
+
+  machine = import ./machine.nix;
+
+  testScript = ''
+    startAll;
+
+    $machine->waitForUnit("sockets.target");
+    $machine->waitUntilSucceeds("docker run registry-1.docker.io/v2/library/hello-world:latest");
+  '';
+})
diff --git a/nixos/tests/hocker-fetchdocker/hello-world-container.nix b/nixos/tests/hocker-fetchdocker/hello-world-container.nix
new file mode 100644
index 000000000000..a127875264e9
--- /dev/null
+++ b/nixos/tests/hocker-fetchdocker/hello-world-container.nix
@@ -0,0 +1,19 @@
+{ fetchDockerConfig, fetchDockerLayer, fetchdocker }:
+fetchdocker rec {
+    name = "hello-world";
+    registry = "https://registry-1.docker.io/v2/";
+    repository = "library";
+    imageName = "hello-world";
+    tag = "latest";
+    imageConfig = fetchDockerConfig {
+      inherit tag registry repository imageName;
+      sha256 = "1ivbd23hyindkahzfw4kahgzi6ibzz2ablmgsz6340vc6qr1gagj";
+    };
+    imageLayers = let
+      layer0 = fetchDockerLayer {
+        inherit registry repository imageName;
+        layerDigest = "ca4f61b1923c10e9eb81228bd46bee1dfba02b9c7dac1844527a734752688ede";
+        sha256 = "1plfd194fwvsa921ib3xkhms1yqxxrmx92r2h7myj41wjaqn2kya";
+      };
+      in [ layer0 ];
+  }
diff --git a/nixos/tests/hocker-fetchdocker/machine.nix b/nixos/tests/hocker-fetchdocker/machine.nix
new file mode 100644
index 000000000000..12c58a012243
--- /dev/null
+++ b/nixos/tests/hocker-fetchdocker/machine.nix
@@ -0,0 +1,26 @@
+{ config, pkgs, ... }:
+{ nixpkgs.config.packageOverrides = pkgs': {
+    hello-world-container = pkgs'.callPackage ./hello-world-container.nix { };
+  };
+
+  virtualisation.docker = {
+    enable  = true;
+    package = pkgs.docker;
+  };
+
+  systemd.services.docker-load-fetchdocker-image = {
+    description = "Docker load hello-world-container";
+    wantedBy    = [ "multi-user.target" ];
+    wants       = [ "docker.service" "local-fs.target" ];
+    after       = [ "docker.service" "local-fs.target" ];
+
+    script = ''
+      ${pkgs.hello-world-container}/compositeImage.sh | ${pkgs.docker}/bin/docker load
+    '';
+
+    serviceConfig = {
+      Type = "oneshot";
+    };
+  };
+}
+
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
new file mode 100644
index 000000000000..2e45dc78471f
--- /dev/null
+++ b/nixos/tests/home-assistant.nix
@@ -0,0 +1,76 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+let
+  configDir = "/var/lib/foobar";
+  apiPassword = "secret";
+
+in {
+  name = "home-assistant";
+  meta = with pkgs.stdenv.lib; {
+    maintainers = with maintainers; [ dotlambda ];
+  };
+
+  nodes = {
+    hass =
+      { config, pkgs, ... }:
+      {
+        environment.systemPackages = with pkgs; [
+          mosquitto
+        ];
+        services.home-assistant = {
+          inherit configDir;
+          enable = true;
+          package = pkgs.home-assistant.override {
+            extraPackages = ps: with ps; [ hbmqtt ];
+          };
+          config = {
+            homeassistant = {
+              name = "Home";
+              time_zone = "UTC";
+              latitude = "0.0";
+              longitude = "0.0";
+              elevation = 0;
+            };
+            frontend = { };
+            http.api_password = apiPassword;
+            mqtt = { }; # Use hbmqtt as broker
+            binary_sensor = [
+              {
+                platform = "mqtt";
+                state_topic = "home-assistant/test";
+                payload_on = "let_there_be_light";
+                payload_off = "off";
+              }
+            ];
+          };
+        };
+      };
+  };
+
+  testScript = ''
+    startAll;
+    $hass->waitForUnit("home-assistant.service");
+
+    # Since config is specified using a Nix attribute set,
+    # configuration.yaml is a link to the Nix store
+    $hass->succeed("test -L ${configDir}/configuration.yaml");
+
+    # Check that Home Assistant's web interface and API can be reached
+    $hass->waitForOpenPort(8123);
+    $hass->succeed("curl --fail http://localhost:8123/states");
+    $hass->succeed("curl --fail -H 'x-ha-access: ${apiPassword}' http://localhost:8123/api/ | grep -qF 'API running'");
+
+    # Toggle a binary sensor using MQTT
+    $hass->succeed("curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}' | grep -qF '\"state\": \"off\"'");
+    $hass->waitUntilSucceeds("mosquitto_pub -V mqttv311 -t home-assistant/test -u homeassistant -P '${apiPassword}' -m let_there_be_light");
+    $hass->succeed("curl http://localhost:8123/api/states/binary_sensor.mqtt_binary_sensor -H 'x-ha-access: ${apiPassword}' | grep -qF '\"state\": \"on\"'");
+
+    # Check that no errors were logged
+    $hass->fail("cat ${configDir}/home-assistant.log | grep -qF ERROR");
+
+    # Print log to ease debugging
+    my $log = $hass->succeed("cat ${configDir}/home-assistant.log");
+    print "\n### home-assistant.log ###\n";
+    print "$log\n";
+  '';
+})
diff --git a/nixos/tests/iftop.nix b/nixos/tests/iftop.nix
new file mode 100644
index 000000000000..21ff3cafed7c
--- /dev/null
+++ b/nixos/tests/iftop.nix
@@ -0,0 +1,30 @@
+import ./make-test.nix ({ pkgs, lib, ... }:
+
+with lib;
+
+{
+  name = "iftop";
+  meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ];
+
+  nodes = {
+    withIftop = {
+      imports = [ ./common/user-account.nix ];
+
+      programs.iftop.enable = true;
+    };
+    withoutIftop = {
+      imports = [ ./common/user-account.nix ];
+    };
+  };
+
+  testScript = ''
+    subtest "machine with iftop enabled", sub {
+      $withIftop->start;
+      $withIftop->succeed("su -l alice -c 'iftop -t -s 1'");
+    };
+    subtest "machine without iftop", sub {
+      $withoutIftop->start;
+      $withoutIftop->mustFail("su -l alice -c 'iftop -t -s 1'");
+    };
+  '';
+})
diff --git a/nixos/tests/initrd-network-ssh.nix b/nixos/tests/initrd-network-ssh/default.nix
index 596610493921..b1f3d147e862 100644
--- a/nixos/tests/initrd-network-ssh.nix
+++ b/nixos/tests/initrd-network-ssh/default.nix
@@ -1,19 +1,6 @@
-import ./make-test.nix ({ pkgs, lib, ... }:
+import ../make-test.nix ({ pkgs, lib, ... }:
 
-let
-  keys = pkgs.runCommand "gen-keys" {
-    outputs = [ "out" "dbPub" "dbPriv" "sshPub" "sshPriv" ];
-    buildInputs = with pkgs; [ dropbear openssh ];
-  }
-  ''
-    touch $out
-    dropbearkey -t rsa -f $dbPriv -s 4096 | sed -n 2p > $dbPub
-    ssh-keygen -q -t rsa -b 4096 -N "" -f client
-    mv client $sshPriv
-    mv client.pub $sshPub
-  '';
-
-in {
+{
   name = "initrd-network-ssh";
   meta = with lib.maintainers; {
     maintainers = [ willibutz ];
@@ -24,17 +11,15 @@ in {
       { config, pkgs, ... }:
       {
         boot.kernelParams = [
-          "ip=${
-            (head config.networking.interfaces.eth1.ip4).address
-          }:::255.255.255.0::eth1:none"
+          "ip=${config.networking.primaryIPAddress}:::255.255.255.0::eth1:none"
         ];
         boot.initrd.network = {
           enable = true;
           ssh = {
             enable = true;
-            authorizedKeys = [ "${readFile keys.sshPub}" ];
+            authorizedKeys = [ "${readFile ./openssh.pub}" ];
             port = 22;
-            hostRSAKey = keys.dbPriv;
+            hostRSAKey = ./dropbear.priv;
           };
         };
         boot.initrd.preLVMCommands = ''
@@ -56,7 +41,7 @@ in {
             "${toString (head (splitString " " (
               toString (elemAt (splitString "\n" config.networking.extraHosts) 2)
             )))} "
-            "${readFile keys.dbPub}"
+            "${readFile ./dropbear.pub}"
           ];
         };
       };
@@ -65,7 +50,7 @@ in {
   testScript = ''
     startAll;
     $client->waitForUnit("network.target");
-    $client->copyFileFromHost("${keys.sshPriv}","/etc/sshKey");
+    $client->copyFileFromHost("${./openssh.priv}","/etc/sshKey");
     $client->succeed("chmod 0600 /etc/sshKey");
     $client->waitUntilSucceeds("ping -c 1 server");
     $client->succeed("ssh -i /etc/sshKey -o UserKnownHostsFile=/etc/knownHosts server 'touch /fnord'");
diff --git a/nixos/tests/initrd-network-ssh/dropbear.priv b/nixos/tests/initrd-network-ssh/dropbear.priv
new file mode 100644
index 000000000000..af340535f0a3
--- /dev/null
+++ b/nixos/tests/initrd-network-ssh/dropbear.priv
Binary files differdiff --git a/nixos/tests/initrd-network-ssh/dropbear.pub b/nixos/tests/initrd-network-ssh/dropbear.pub
new file mode 100644
index 000000000000..385c625522aa
--- /dev/null
+++ b/nixos/tests/initrd-network-ssh/dropbear.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzJ0OniLB91MpPC86I1m3wwJeAc+Gme7bAuaLIU/cSfPwxT5NO7MfCp0Pu94gYDKtDXMs/wXg0bTAVDeAFFkdIj6kBBumEmQLCTL48q2UxDIXVLT/E/AAgj6q7WwgCg7fwm4Vjn4z7aUyBx8EfRy+5/SQyeYla3D/lFYgMi5x4D6J+yeR+JPAptDE/IR5IizNV7mY0ZcoXYyHrrehI1tTYEEqjX13ZqS4OCBFWwHe1QHhRNM+jHhcATbgikjAj8FyFPtLvc+dSVtkuhQktQl36Bi8zMUQcV6+mM7Ln6DBcDlM9urHKLYPTWmUAyhxM955iglOn5z0RaAIcyNMT6hz0rHaNf0BIlmbXoTC0XGjHh/OnoOEC/zg0JqgQTnPiU45K4TnRSSXp2GfiDfiQAK0+HaXACkjuFR68u7WCZpB1Bse1OgKNClFqtRhIr5DilUb2/e5DCCmFkddMUcjmYqzZdbXNt7fo8CFULe+mbiCp8+tMg4aRTaDZ/Hk93nCvGE5TP2ypEMbfL6nRVKvXOjhdvSQQgKwx+O003FDEHCSG0Bpageh7yVpna+SPrbGklce7MjTpbx3iIwmvKpQ6asnK1L3KkahpY1S3NhQ+/S3Gs8KWQ5LAU+d3xiPX3jfIVHsCIIyxHDbwcJvxM4MFBFQpqRMD6E+LoM9RHjl4C9k2iQ== tmtynkky@duuni
diff --git a/nixos/tests/initrd-network-ssh/generate-keys.nix b/nixos/tests/initrd-network-ssh/generate-keys.nix
new file mode 100644
index 000000000000..0183e12d7a88
--- /dev/null
+++ b/nixos/tests/initrd-network-ssh/generate-keys.nix
@@ -0,0 +1,12 @@
+with import ../../.. {};
+
+runCommand "gen-keys" {
+    buildInputs = [ dropbear openssh ];
+  }
+  ''
+    mkdir $out
+    dropbearkey -t rsa -f $out/dropbear.priv -s 4096 | sed -n 2p > $out/dropbear.pub
+    ssh-keygen -q -t rsa -b 4096 -N "" -f client
+    mv client $out/openssh.priv
+    mv client.pub $out/openssh.pub
+  ''
diff --git a/nixos/tests/initrd-network-ssh/openssh.priv b/nixos/tests/initrd-network-ssh/openssh.priv
new file mode 100644
index 000000000000..816d65435fd7
--- /dev/null
+++ b/nixos/tests/initrd-network-ssh/openssh.priv
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA7+9A2PCPOTAlFmrablrUWA+VZdAuLfM6JXeHsOF7ZbC2F6lv
+WmvDM925DQqhiAjcgWnt5WHWS5Y+b7lGnuzT7fyKegXd80nCRmqlpSG3srX0/lxR
+aQAJLzfoDjcsF+ceswQo6GSsYnCHVxMNs007gbbVY3f7o+sWZtLdxJPD2iHvl5Zr
+LK0d1RLMmU6cfIhIABlL0S8EWiv29RROepsCQnS0dnK2b+von1SCYoggvAMe2ToA
+IAJ8+uqaYfGAyn9q8fjZiRHxLmKDq90tKoCUL5r/2dmEIE+t8T/3PfHoq1QzZts9
+W9idhBdT21dEXBtGyoMtckp5njk5m82LQDYiOXkuSoIUhSOteh5g7fBv1BtVSERx
+Jg3UeJjPeGKFwdnzapmAKC2w/6V8xcIINNA+fhZA7B9fD1RAi2TECZ+gyMYDc4T+
+USlMSm9cfvSOrf2+5ngtFb84nHjqvClxCMLu+bCWK8HamqUzhE/a5LbR+48E7PyG
+s3KV+sWFN9KOnakTjj/6iQhXZRhgeAK39F2XTk5Ms5Y+BRSStnMoMZA2grIV+jHi
+1zbWokVqXPI5YRo5isR/PgtKAV6FfNWumcYoFJ9F40pMHQ6hJVEmtrCBx7EApSl3
+mSGbQJUmilLC51qNhwQRbD//ZtpIrN82HTMKzZ6kj7kDCdsff+wsnkIXmmMCAwEA
+AQKCAgA4tMINw6UF7hQF3VEsnbjr6xrzCiWv5HlMm5htPI1OdlpC81+G7ksfOfrf
+UzDkFrwOtftsqBfem268Nvyy2OQprfMIbdSMCFWrEM9/XJ2u1gRGDYmMGF8TUtI8
+cduw9oWx53zHl+uKBHBoKu+k/c7flFeQf63wisIroRCawhWau0SF/h3sXCndzuie
+Hw8q+4aQx2m80bDkotlmCNuXbIU3MZ/pEql9gDLlXTLHmMaryM0EqAmZhx0ErGe6
+WDqJIV4kPB0loSDwRoY6GzbugZ8ENUzcruTkQhCpIOYNNNw5idfwKkaxK1vm+SBv
+iYt1fVjYyfH2vhVKSNoNsaGEloa1u4Dymt/FpFztEpRzHXcw93N8BdLxJ4OUhzm2
+iAbpiyjniTIeAVVi7BUwLXh5WAx8nT0eeb1zKoZg1p1ciK5cYl1Uel7j8xRycsSW
+3YgmtuPqY4Agbc9v3eXbQZNDk48JFMEqpIxk97FAkRYpzfxg5Qq14WJCp60CkdRt
+T60hXy8lT/BcI8OWLfGJuBbsVLNRiC7PpwqRKQAinXSv134FpP7jrhpkMybs2oIS
+5obRG7J5OfOTp925erG5mrpwqa3BPkgqx347Wj9z8quOZyuhi+XaPvqmPtvs5JOl
+4RCqjt6RQlHm7xos9ZZGI4jDAIFaFWgyVZrYplOgwxWma4DTgQKCAQEA9+tizQRU
+lF0lxNcEPvsFnYJo80Y+MQK9VdtlhR19YuSfwP1NCaMG1MhQ+PVBVmepOwJMRJR7
+9PLfOouNMfixKBGP12dtStMuh7jowq/BxhRI6JWp3RhTZ1yJ9ouzHze7IDrEBa6w
+p0hUu9H0Sbt51LXbC3JmTyhbdhfry559DfyGW1Ma/bv/pihL9B5Y7sNf1thNp1gi
+GbQ9B+o2Yyw8ZD8zY+sl+aYDSWyCtcBV/KXEF74Bkfs/a5ExJ00X0jYj/TAp2ray
+T4PY0FR8wN/O10bFLP9j+Xa/ywbcPhoj8nvVRIg9VfWT/QaEd+KR0EZVxdjCCqne
+enbSQksTpAZNwQKCAQEA98E+BMmS+yHUVUhNZABtQ5avwuV4+DoSN8KTp3xwQ0CH
+m9fWxSDs12FdyMhDxrJPeywvHtZ18/7cl3dr8wnFVE0s4ongnRDXsNk5xN6J3AaO
+KqW4HF9cbwZqzLILy8TrO+EK/EQV9FypbrxqvxAlP1kezIA2CJNzVRAgimSuV/H7
+05HTnp5W06fjtEf8U1CUrdNetoSROUo1j/IMGPYGlsBFYAGrj5y/BlKd+3T3kjRp
+Xje7HpiykjrZHn0WDp04Ln+u9nveEewXmHKch313emt7HpW0xspp8JM8OZtEKozk
+D5PfYdBfMJJOUlqovCCzTTJ6kNOahknKXFeO/qs5IwKCAQEAjF0/zhWikXF/fcfD
+Bql2z2vTYdEmSvdjHSYff1Nn90K71DdVk5wytOxJM/sfp/z+yoMNjVKIL/IGQw5Z
+va4xFx+CUhGjxlZ0pLEjT37U9gHsGYsK5jvslLvG/MixfH5AOwoqi5ERQVTpbIF9
+jvVPEAh6YSu/ExglWGJIxTsRUIblxvTxdjEnl/p+rlM0RNJnA6vpo1J51BXA7CdF
+7bZQ5u0Feo/bK1I70ClYg/DGfkmYEV0pZG5cxNkqfDbgwsqWa7YGLGd94xkh+ymq
+jETqxeWyozxhbQ83nYpfzeVc7t//qlJ8b5uf0wUKoRmtNr9rtp13lzP/21REzPXW
+w+oxwQKCAQAoAf2Y2lAw25KlPuq4ZlU+n9u8FkBFnWMJvBMJ7c9XHNmJMf6NkLaO
+RTvWy3geYvbwxf7J9QnRH+vRTciR05cY+Olxn6A03N5nwXxRrToH3MsiWeZ0NnX/
+u8KNUYcUHbV60ulqOThuYHQ/3I9EUUAijaqqjV2sXts19ke68W0x6HKpBJhuudT9
+ktPzbdhyP8Xyl/pocNnerXwexZBsi3Ye6+eIDFz+8OnsBHVcgNPluS72tvsxgqj7
+ciNTiBGCxKKo55eCWBhRPpXE2WUrf/hGPYsBMl2h6FfZMH1+M/N7B4tgdJmS+woU
+Ftws8lTjJEiwA6HFN1ZxrwLNjJobx9yPAoIBAE0igsBuWWn6rXeOPylYg4264XOq
+8gb94pte2n9amDgCzyCn8m6AL3snLC/AoCD19DK+gyK0ukoesXPa3iX6w2xv69ZC
+urDx36Jhd4zrJb4QsFPoeKfDP+UvNVZaS41vipRRzY/y11em15prUZ4U8FA/UT1Y
+FzkBo9r6iUZRnyBLppMuEfWASDtuRNmeIHynoT1AcQOH3l9vR210iEpmAuJr0CYA
+bvTuz3UzzGGEAuIUvuaiRtkfKY52jBmiEr7SSPCr1HvLj3Ccz8bgjgR2kiXmcU50
+1zLnaPAD44LZ/0Fjqj+PimQGT6K7CNXPllmYh7MvoU52g3SVPf6rHlIR0Nc=
+-----END RSA PRIVATE KEY-----
diff --git a/nixos/tests/initrd-network-ssh/openssh.pub b/nixos/tests/initrd-network-ssh/openssh.pub
new file mode 100644
index 000000000000..5b72b8085f27
--- /dev/null
+++ b/nixos/tests/initrd-network-ssh/openssh.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDv70DY8I85MCUWatpuWtRYD5Vl0C4t8zold4ew4XtlsLYXqW9aa8Mz3bkNCqGICNyBae3lYdZLlj5vuUae7NPt/Ip6Bd3zScJGaqWlIbeytfT+XFFpAAkvN+gONywX5x6zBCjoZKxicIdXEw2zTTuBttVjd/uj6xZm0t3Ek8PaIe+XlmssrR3VEsyZTpx8iEgAGUvRLwRaK/b1FE56mwJCdLR2crZv6+ifVIJiiCC8Ax7ZOgAgAnz66pph8YDKf2rx+NmJEfEuYoOr3S0qgJQvmv/Z2YQgT63xP/c98eirVDNm2z1b2J2EF1PbV0RcG0bKgy1ySnmeOTmbzYtANiI5eS5KghSFI616HmDt8G/UG1VIRHEmDdR4mM94YoXB2fNqmYAoLbD/pXzFwgg00D5+FkDsH18PVECLZMQJn6DIxgNzhP5RKUxKb1x+9I6t/b7meC0VvziceOq8KXEIwu75sJYrwdqapTOET9rkttH7jwTs/IazcpX6xYU30o6dqROOP/qJCFdlGGB4Arf0XZdOTkyzlj4FFJK2cygxkDaCshX6MeLXNtaiRWpc8jlhGjmKxH8+C0oBXoV81a6ZxigUn0XjSkwdDqElUSa2sIHHsQClKXeZIZtAlSaKUsLnWo2HBBFsP/9m2kis3zYdMwrNnqSPuQMJ2x9/7CyeQheaYw== tmtynkky@duuni
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 43b166a38374..acf248d0a5a6 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
@@ -70,13 +69,20 @@ let
     let
       iface = if grubVersion == 1 then "ide" else "virtio";
       isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
+
+      # FIXME don't duplicate the -enable-kvm etc. flags here yet again!
       qemuFlags =
         (if system == "x86_64-linux" then "-m 768 " else "-m 512 ") +
-        (optionalString (system == "x86_64-linux") "-cpu kvm64 ");
+        (optionalString (system == "x86_64-linux") "-cpu kvm64 ") +
+        (optionalString (system == "aarch64-linux") "-enable-kvm -machine virt,gic-version=host -cpu host ");
+
       hdFlags = ''hda => "vm-state-machine/machine.qcow2", hdaInterface => "${iface}", ''
-        + optionalString isEfi ''bios => "${pkgs.OVMF.fd}/FV/OVMF.fd", '';
-    in
-    ''
+        + optionalString isEfi (if pkgs.stdenv.isAarch64
+            then ''bios => "${pkgs.OVMF.fd}/FV/QEMU_EFI.fd", ''
+            else ''bios => "${pkgs.OVMF.fd}/FV/OVMF.fd", '');
+    in if !isEfi && !(pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then
+      throw "Non-EFI boot methods are only supported on i686 / x86_64"
+    else ''
       $machine->start;
 
       # Make sure that we get a login prompt etc.
@@ -146,7 +152,7 @@ let
       # Check that the daemon works, and that non-root users can run builds (this will build a new profile generation through the daemon)
       $machine->succeed("su alice -l -c 'nix-env -iA nixos.procps' >&2");
 
-      # We need to a writable nix-store on next boot.
+      # We need a writable Nix store on next boot.
       $machine->copyFileFromHost(
           "${ makeConfig { inherit bootLoader grubVersion grubDevice grubIdentifier grubUseEfi extraConfig; forceGrubReinstallCount = 1; } }",
           "/etc/nixos/configuration.nix");
@@ -196,8 +202,7 @@ let
       };
       nodes = {
 
-        # The configuration of the machine used to run "nixos-install". It
-        # also has a web server that simulates cache.nixos.org.
+        # The configuration of the machine used to run "nixos-install".
         machine =
           { config, lib, pkgs, ... }:
 
@@ -209,7 +214,6 @@ let
 
             virtualisation.diskSize = 8 * 1024;
             virtualisation.memorySize = 1024;
-            virtualisation.writableStore = true;
 
             # Use a small /dev/vdb as the root disk for the
             # installer. This ensures the target disk (/dev/vda) is
@@ -246,6 +250,11 @@ let
               ++ optionals (bootLoader == "grub" && grubVersion == 2) [ pkgs.grub2 pkgs.grub2_efi ];
 
             nix.binaryCaches = mkForce [ ];
+            nix.extraOptions =
+              ''
+                hashed-mirrors =
+                connect-timeout = 1
+              '';
           };
 
       };
@@ -307,11 +316,11 @@ in {
     { createPartitions =
         ''
           $machine->succeed(
-              "parted /dev/vda mklabel gpt",
-              "parted -s /dev/vda -- mkpart ESP fat32 1M 50MiB", # /boot
-              "parted -s /dev/vda -- set 1 boot on",
-              "parted -s /dev/vda -- mkpart primary linux-swap 50MiB 1024MiB",
-              "parted -s /dev/vda -- mkpart primary ext2 1024MiB -1MiB", # /
+              "parted --script /dev/vda mklabel gpt",
+              "parted --script /dev/vda -- mkpart ESP fat32 1M 50MiB", # /boot
+              "parted --script /dev/vda -- set 1 boot on",
+              "parted --script /dev/vda -- mkpart primary linux-swap 50MiB 1024MiB",
+              "parted --script /dev/vda -- mkpart primary ext2 1024MiB -1MiB", # /
               "udevadm settle",
               "mkswap /dev/vda2 -L swap",
               "swapon -L swap",
@@ -461,6 +470,47 @@ in {
       '';
     };
 
+  # Test whether opening encrypted filesystem with keyfile
+  # Checks for regression of missing cryptsetup, when no luks device without
+  # keyfile is configured
+  filesystemEncryptedWithKeyfile = makeInstallerTest "filesystemEncryptedWithKeyfile"
+    { createPartitions = ''
+       $machine->succeed(
+          "parted --script /dev/vda mklabel msdos",
+          "parted --script /dev/vda -- mkpart primary ext2 1M 50MB", # /boot
+          "parted --script /dev/vda -- mkpart primary linux-swap 50M 1024M",
+          "parted --script /dev/vda -- mkpart primary 1024M 1280M", # LUKS with keyfile
+          "parted --script /dev/vda -- mkpart primary 1280M -1s",
+          "udevadm settle",
+          "mkswap /dev/vda2 -L swap",
+          "swapon -L swap",
+          "mkfs.ext3 -L nixos /dev/vda4",
+          "mount LABEL=nixos /mnt",
+          "mkfs.ext3 -L boot /dev/vda1",
+          "mkdir -p /mnt/boot",
+          "mount LABEL=boot /mnt/boot",
+          "modprobe dm_mod dm_crypt",
+          "echo -n supersecret > /mnt/keyfile",
+          "cryptsetup luksFormat -q /dev/vda3 --key-file /mnt/keyfile",
+          "cryptsetup luksOpen --key-file /mnt/keyfile /dev/vda3 crypt",
+          "mkfs.ext3 -L test /dev/mapper/crypt",
+          "cryptsetup luksClose crypt",
+          "mkdir -p /mnt/test"
+        );
+      '';
+      extraConfig = ''
+        fileSystems."/test" =
+        { device = "/dev/disk/by-label/test";
+          fsType = "ext3";
+          encrypted.enable = true;
+          encrypted.blkDev = "/dev/vda3";
+          encrypted.label = "crypt";
+          encrypted.keyFile = "/mnt-root/keyfile";
+        };
+      '';
+    };
+
+
   swraid = makeInstallerTest "swraid"
     { createPartitions =
         ''
@@ -469,10 +519,10 @@ in {
               . " mklabel msdos"
               . " mkpart primary ext2 1M 100MB" # /boot
               . " mkpart extended 100M -1s"
-              . " mkpart logical 102M 1602M" # md0 (root), first device
-              . " mkpart logical 1603M 3103M" # md0 (root), second device
-              . " mkpart logical 3104M 3360M" # md1 (swap), first device
-              . " mkpart logical 3361M 3617M", # md1 (swap), second device
+              . " mkpart logical 102M 2102M" # md0 (root), first device
+              . " mkpart logical 2103M 4103M" # md0 (root), second device
+              . " mkpart logical 4104M 4360M" # md1 (swap), first device
+              . " mkpart logical 4361M 4617M", # md1 (swap), second device
               "udevadm settle",
               "ls -l /dev/vda* >&2",
               "cat /proc/partitions >&2",
diff --git a/nixos/tests/ipv6.nix b/nixos/tests/ipv6.nix
index 060f63216796..7a98fd85cfda 100644
--- a/nixos/tests/ipv6.nix
+++ b/nixos/tests/ipv6.nix
@@ -47,7 +47,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       # Detection).
       sub waitForAddress {
           my ($machine, $iface, $scope) = @_;
-          $machine->waitUntilSucceeds("[ `ip -o -6 addr show dev $iface scope $scope | grep -v tentative | wc -l` -eq 1 ]");
+          $machine->waitUntilSucceeds("[ `ip -o -6 addr show dev $iface scope $scope | grep -v tentative | wc -l` -ge 1 ]");
           my $ip = (split /[ \/]+/, $machine->succeed("ip -o -6 addr show dev $iface scope $scope"))[3];
           $machine->log("$scope address on $iface is $ip");
           return $ip;
diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix
index a9833058f378..ed55b2ff5871 100644
--- a/nixos/tests/jenkins.nix
+++ b/nixos/tests/jenkins.nix
@@ -36,6 +36,9 @@ import ./make-test.nix ({ pkgs, ...} : {
     startAll;
 
     $master->waitForUnit("jenkins");
+
+    $master->mustSucceed("curl http://localhost:8080 | grep 'Authentication required'");
+
     print $master->execute("sudo -u jenkins groups");
     $master->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users");
 
@@ -44,4 +47,4 @@ import ./make-test.nix ({ pkgs, ...} : {
 
     $slave->mustFail("systemctl is-enabled jenkins.service");
   '';
-})
\ No newline at end of file
+})
diff --git a/nixos/tests/kernel-copperhead.nix b/nixos/tests/kernel-copperhead.nix
index 07427d7f2a89..0af978f1851f 100644
--- a/nixos/tests/kernel-copperhead.nix
+++ b/nixos/tests/kernel-copperhead.nix
@@ -6,14 +6,14 @@ import ./make-test.nix ({ pkgs, ...} : {
 
   machine = { config, lib, pkgs, ... }:
     {
-      boot.kernelPackages = pkgs.linuxPackages_hardened_copperhead;
+      boot.kernelPackages = pkgs.linuxPackages_copperhead_hardened;
     };
 
   testScript =
     ''
       $machine->succeed("uname -a");
       $machine->succeed("uname -s | grep 'Linux'");
-      $machine->succeed("uname -a | grep '${pkgs.linuxPackages_hardened_copperhead.kernel.modDirVersion}'");
+      $machine->succeed("uname -a | grep '${pkgs.linuxPackages_copperhead_hardened.kernel.modDirVersion}'");
       $machine->succeed("uname -a | grep 'hardened'");
     '';
 })
diff --git a/nixos/tests/keymap.nix b/nixos/tests/keymap.nix
index c431c1a34174..caa5f7107c23 100644
--- a/nixos/tests/keymap.nix
+++ b/nixos/tests/keymap.nix
@@ -46,41 +46,11 @@ let
   in makeTest {
     name = "keymap-${layout}";
 
+    machine.services.xserver.desktopManager.xterm.enable = false;
     machine.i18n.consoleKeyMap = mkOverride 900 layout;
     machine.services.xserver.layout = mkOverride 900 layout;
     machine.imports = [ ./common/x11.nix extraConfig ];
-    machine.services.xserver.displayManager.slim = {
-      enable = true;
-
-      # Use a custom theme in order to get best OCR results
-      theme = pkgs.runCommand "slim-theme-ocr" {
-        nativeBuildInputs = [ pkgs.imagemagick ];
-      } ''
-        mkdir "$out"
-        convert -size 1x1 xc:white "$out/background.jpg"
-        convert -size 200x100 xc:white "$out/panel.jpg"
-        cat > "$out/slim.theme" <<EOF
-        background_color #ffffff
-        background_style tile
-
-        input_fgcolor #000000
-        msg_color #000000
-
-        session_color #000000
-        session_font Verdana:size=16:bold
-
-        username_msg Username:
-        username_font Verdana:size=16:bold
-        username_color #000000
-        username_x 50%
-        username_y 40%
-
-        password_msg Password:
-        password_x 50%
-        password_y 40%
-        EOF
-      '';
-    };
+    machine.services.xserver.displayManager.slim.enable = true;
 
     testScript = ''
       sub waitCatAndDelete ($) {
diff --git a/nixos/tests/keystone.nix b/nixos/tests/keystone.nix
deleted file mode 100644
index 358e352f776f..000000000000
--- a/nixos/tests/keystone.nix
+++ /dev/null
@@ -1,82 +0,0 @@
-{ system ? builtins.currentSystem }:
-
-with import ../lib/testing.nix { inherit system; };
-with pkgs.lib;
-
-let
-  keystoneMysqlPassword = "keystoneMysqlPassword";
-  keystoneMysqlPasswordFile = "/var/run/keystoneMysqlPassword";
-  keystoneAdminPassword = "keystoneAdminPassword";
-
-  createKeystoneDb = pkgs.writeText "create-keystone-db.sql" ''
-    create database keystone;
-    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY '${keystoneMysqlPassword}';
-    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY '${keystoneMysqlPassword}';
-  '';
-  # The admin keystone account
-  adminOpenstackCmd = "OS_TENANT_NAME=admin OS_USERNAME=admin OS_PASSWORD=${keystoneAdminPassword} OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
-  # The created demo keystone account
-  demoOpenstackCmd = "OS_TENANT_NAME=demo OS_USERNAME=demo OS_PASSWORD=demo OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
-
-in makeTest {
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ lewo ];
-  };
-  machine =
-    { config, pkgs, ... }:
-    {
-      # This is to simulate nixops deployment process.
-      # https://nixos.org/nixops/manual/#opt-deployment.keys
-      boot.postBootCommands = "echo ${keystoneMysqlPassword} > ${keystoneMysqlPasswordFile}";
-
-      services.mysql.enable = true;
-      services.mysql.initialScript = createKeystoneDb;
-
-      virtualisation = {
-
-        openstack.keystone = {
-	  enable = true;
-	  # Check if we can get the secret from a file
-	  database.password = {
-	    value = keystoneMysqlPasswordFile;
-	    storage = "fromFile";
-	  };
-	  adminToken = {
-	    value = "adminToken";
-	    storage = "fromNixStore";
-	  };
-
-	  bootstrap.enable = true;
-	  # Check if we can get the secret from the store
-	  bootstrap.adminPassword = {
-	    value = keystoneAdminPassword;
-	    storage = "fromNixStore";
-	  };
-	};
-
-        memorySize = 2096;
-        diskSize = 4 * 1024;
-	};
-
-      environment.systemPackages = with pkgs.pythonPackages; with pkgs; [
-        openstackclient
-      ];
-    };
-
-  testScript =
-    ''
-     $machine->waitForUnit("keystone-all.service");
-
-     # Verify that admin ccount is working
-     $machine->succeed("${adminOpenstackCmd} token issue");
-
-     # Try to create a new user
-     $machine->succeed("${adminOpenstackCmd} project create --domain default --description 'Demo Project' demo");
-     $machine->succeed("${adminOpenstackCmd} user create --domain default --password demo demo");
-     $machine->succeed("${adminOpenstackCmd} role create user");
-     $machine->succeed("${adminOpenstackCmd} role add --project demo --user demo user");
-
-     # Verify this new account is working
-     $machine->succeed("${demoOpenstackCmd} token issue");
-    '';
-}
diff --git a/nixos/tests/kubernetes/base.nix b/nixos/tests/kubernetes/base.nix
index acf2e0250819..27b99aacab7d 100644
--- a/nixos/tests/kubernetes/base.nix
+++ b/nixos/tests/kubernetes/base.nix
@@ -1,14 +1,13 @@
 { system ? builtins.currentSystem }:
 
 with import ../../lib/testing.nix { inherit system; };
-with import ../../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
   mkKubernetesBaseTest =
     { name, domain ? "my.zyx", test, machines
     , pkgs ? import <nixpkgs> { inherit system; }
-    , certs ? import ./certs.nix { inherit pkgs; externalDomain = domain; }
+    , certs ? import ./certs.nix { inherit pkgs; externalDomain = domain; kubelets = attrNames machines; }
     , extraConfiguration ? null }:
     let
       masterName = head (filter (machineName: any (role: role == "master") machines.${machineName}.roles) (attrNames machines));
diff --git a/nixos/tests/kubernetes/certs.nix b/nixos/tests/kubernetes/certs.nix
index f108e35b98cd..520c728b65ee 100644
--- a/nixos/tests/kubernetes/certs.nix
+++ b/nixos/tests/kubernetes/certs.nix
@@ -2,32 +2,66 @@
   pkgs ? import <nixpkgs> {},
   internalDomain ? "cloud.yourdomain.net",
   externalDomain ? "myawesomecluster.cluster.yourdomain.net",
-  serviceClusterIp ? "10.0.0.1"
+  serviceClusterIp ? "10.0.0.1",
+  kubelets
 }:
 let
-  runWithCFSSL = name: cmd:
-    builtins.fromJSON (builtins.readFile (
-      pkgs.runCommand "${name}-cfss.json" {
-        buildInputs = [ pkgs.cfssl ];
-      } "cfssl ${cmd} > $out"
-    ));
-
-  writeCFSSL = content:
-    pkgs.runCommand content.name {
-      buildInputs = [ pkgs.cfssl ];
-    } ''
-      mkdir -p $out
-      cd $out
-      cat ${writeFile content} | cfssljson -bare ${content.name}
-    '';
+   runWithCFSSL = name: cmd:
+     let secrets = pkgs.runCommand "${name}-cfss.json" {
+         buildInputs = [ pkgs.cfssl pkgs.jq ];
+         outputs = [ "out" "cert" "key" "csr" ];
+       }
+       ''
+         (
+           echo "${cmd}"
+           cfssl ${cmd} > tmp
+           cat tmp | jq -r .key > $key
+           cat tmp | jq -r .cert > $cert
+           cat tmp | jq -r .csr > $csr
+
+           touch $out
+         ) 2>&1 | fold -w 80 -s
+       '';
+     in {
+       key = secrets.key;
+       cert = secrets.cert;
+       csr = secrets.csr;
+     };
+
+   writeCFSSL = content:
+     pkgs.runCommand content.name {
+      buildInputs = [ pkgs.cfssl pkgs.jq ];
+     } ''
+       mkdir -p $out
+       cd $out
+
+       json=${pkgs.lib.escapeShellArg (builtins.toJSON content)}
+
+       # for a given $field in the $json, treat the associated value as a
+       # file path and substitute the contents thereof into the $json
+       # object.
+       expandFileField() {
+         local field=$1
+         if jq -e --arg field "$field" 'has($field)'; then
+           local path="$(echo "$json" | jq -r ".$field")"
+           json="$(echo "$json" | jq --arg val "$(cat "$path")" ".$field = \$val")"
+         fi
+       }
+
+       expandFileField key
+       expandFileField ca
+       expandFileField cert
+
+       echo "$json" | cfssljson -bare ${content.name}
+     '';
 
   noCSR = content: pkgs.lib.filterAttrs (n: v: n != "csr") content;
   noKey = content: pkgs.lib.filterAttrs (n: v: n != "key") content;
 
-  writeFile = content: pkgs.writeText "content" (
-    if pkgs.lib.isAttrs content then builtins.toJSON content
-    else toString content
-  );
+  writeFile = content:
+    if pkgs.lib.isDerivation content
+    then content
+    else pkgs.writeText "content" (builtins.toJSON content);
 
   createServingCertKey = { ca, cn, hosts? [], size ? 2048, name ? cn }:
     noCSR (
@@ -123,9 +157,10 @@ let
   };
 
   apiserver-client = {
-    kubelet = createClientCertKey {
+    kubelet = hostname: createClientCertKey {
       inherit ca;
-      cn = "apiserver-client-kubelet";
+      name = "apiserver-client-kubelet-${hostname}";
+      cn = "system:node:${hostname}.${externalDomain}";
       groups = ["system:nodes"];
     };
 
@@ -175,10 +210,9 @@ in {
     paths = [
       (writeCFSSL (noKey ca))
       (writeCFSSL kubelet)
-      (writeCFSSL apiserver-client.kubelet)
       (writeCFSSL apiserver-client.kube-proxy)
       (writeCFSSL etcd-client)
-    ];
+    ] ++ map (hostname: writeCFSSL (apiserver-client.kubelet hostname)) kubelets;
   };
 
   admin = writeCFSSL apiserver-client.admin;
diff --git a/nixos/tests/kubernetes/dns.nix b/nixos/tests/kubernetes/dns.nix
index 74d98dabec8d..8c488d271bcd 100644
--- a/nixos/tests/kubernetes/dns.nix
+++ b/nixos/tests/kubernetes/dns.nix
@@ -3,7 +3,7 @@ with import ./base.nix { inherit system; };
 let
   domain = "my.zyx";
 
-  certs = import ./certs.nix { externalDomain = domain; };
+  certs = import ./certs.nix { externalDomain = domain; kubelets = [ "machine1" "machine2" ]; };
 
   redisPod = pkgs.writeText "redis-pod.json" (builtins.toJSON {
     kind = "Pod";
diff --git a/nixos/tests/kubernetes/e2e.nix b/nixos/tests/kubernetes/e2e.nix
index d9d7ba9bb2cc..175d8413045e 100644
--- a/nixos/tests/kubernetes/e2e.nix
+++ b/nixos/tests/kubernetes/e2e.nix
@@ -2,7 +2,7 @@
 with import ./base.nix { inherit system; };
 let
   domain = "my.zyx";
-  certs = import ./certs.nix { externalDomain = domain; };
+  certs = import ./certs.nix { externalDomain = domain; kubelets = ["machine1" "machine2"]; };
   kubeconfig = pkgs.writeText "kubeconfig.json" (builtins.toJSON {
     apiVersion = "v1";
     kind = "Config";
diff --git a/nixos/tests/kubernetes/kubernetes-common.nix b/nixos/tests/kubernetes/kubernetes-common.nix
index 00a5c9aba4e3..ddf427e1b01a 100644
--- a/nixos/tests/kubernetes/kubernetes-common.nix
+++ b/nixos/tests/kubernetes/kubernetes-common.nix
@@ -29,8 +29,8 @@ let
       tlsKeyFile = "${certs.worker}/kubelet-key.pem";
       hostname = "${config.networking.hostName}.${config.networking.domain}";
       kubeconfig = {
-        certFile = "${certs.worker}/apiserver-client-kubelet.pem";
-        keyFile = "${certs.worker}/apiserver-client-kubelet-key.pem";
+        certFile = "${certs.worker}/apiserver-client-kubelet-${config.networking.hostName}.pem";
+        keyFile = "${certs.worker}/apiserver-client-kubelet-${config.networking.hostName}-key.pem";
       };
     };
     controllerManager = {
diff --git a/nixos/tests/kubernetes/rbac.nix b/nixos/tests/kubernetes/rbac.nix
index 1966fed3a5fb..226808c4b263 100644
--- a/nixos/tests/kubernetes/rbac.nix
+++ b/nixos/tests/kubernetes/rbac.nix
@@ -12,7 +12,7 @@ let
   });
 
   roRoleBinding = pkgs.writeText "ro-role-binding.json" (builtins.toJSON {
-    apiVersion = "rbac.authorization.k8s.io/v1beta1";
+    apiVersion = "rbac.authorization.k8s.io/v1";
     kind = "RoleBinding";
     metadata = {
       name = "read-pods";
@@ -31,7 +31,7 @@ let
   });
 
   roRole = pkgs.writeText "ro-role.json" (builtins.toJSON {
-    apiVersion = "rbac.authorization.k8s.io/v1beta1";
+    apiVersion = "rbac.authorization.k8s.io/v1";
     kind = "Role";
     metadata = {
       name = "pod-reader";
diff --git a/nixos/tests/make-test.nix b/nixos/tests/make-test.nix
index f3e26aa7e74d..ee4ba310ad50 100644
--- a/nixos/tests/make-test.nix
+++ b/nixos/tests/make-test.nix
@@ -2,4 +2,4 @@ f: { system ? builtins.currentSystem, ... } @ args:
 
 with import ../lib/testing.nix { inherit system; };
 
-makeTest (if builtins.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f)
+makeTest (if pkgs.lib.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f)
diff --git a/nixos/tests/matrix-synapse.nix b/nixos/tests/matrix-synapse.nix
new file mode 100644
index 000000000000..113fb622588b
--- /dev/null
+++ b/nixos/tests/matrix-synapse.nix
@@ -0,0 +1,30 @@
+import ./make-test.nix ({ pkgs, ... } : {
+
+  name = "matrix-synapse";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ corngood ];
+  };
+
+  nodes = {
+    server_postgres = args: {
+      services.matrix-synapse.enable = true;
+      services.matrix-synapse.database_type = "psycopg2";
+    };
+
+    server_sqlite = args: {
+      services.matrix-synapse.enable = true;
+      services.matrix-synapse.database_type = "sqlite3";
+    };
+  };
+
+  testScript = ''
+    startAll;
+    $server_postgres->waitForUnit("matrix-synapse.service");
+    $server_postgres->waitUntilSucceeds("curl -Lk https://localhost:8448/");
+    $server_postgres->requireActiveUnit("postgresql.service");
+    $server_sqlite->waitForUnit("matrix-synapse.service");
+    $server_sqlite->waitUntilSucceeds("curl -Lk https://localhost:8448/");
+    $server_sqlite->mustSucceed("[ -e /var/lib/matrix-synapse/homeserver.db ]");
+  '';
+
+})
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index 79290861cb0b..179c95e76436 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -1,11 +1,13 @@
 # Miscellaneous small tests that don't warrant their own VM run.
 
-import ./make-test.nix ({ pkgs, ...} : {
+import ./make-test.nix ({ pkgs, ...} : rec {
   name = "misc";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ eelco chaoflow ];
   };
 
+  foo = pkgs.writeText "foo" "Hello World";
+
   machine =
     { config, lib, pkgs, ... }:
     with lib;
@@ -27,10 +29,17 @@ import ./make-test.nix ({ pkgs, ...} : {
       security.sudo = { enable = true; wheelNeedsPassword = false; };
       boot.kernel.sysctl."vm.swappiness" = 1;
       boot.kernelParams = [ "vsyscall=emulate" ];
+      system.extraDependencies = [ foo ];
     };
 
   testScript =
     ''
+      subtest "nix-db", sub {
+          my $json = $machine->succeed("nix path-info --json ${foo}");
+          $json =~ /"narHash":"sha256:0afw0d9j1hvwiz066z93jiddc33nxg6i6qyp26vnqyglpyfivlq5"/ or die "narHash not set";
+          $json =~ /"narSize":128/ or die "narSize not set";
+      };
+
       subtest "nixos-version", sub {
           $machine->succeed("[ `nixos-version | wc -w` = 2 ]");
       };
@@ -87,7 +96,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       $machine->succeed("systemctl start systemd-udev-settle.service");
       subtest "udev-auto-load", sub {
           $machine->waitForUnit('systemd-udev-settle.service');
-          $machine->succeed('lsmod | grep psmouse');
+          $machine->succeed('lsmod | grep mousedev');
       };
 
       # Test whether systemd-tmpfiles-clean works.
@@ -115,11 +124,6 @@ import ./make-test.nix ({ pkgs, ...} : {
           $machine->succeed("nix-store -qR /run/current-system | grep nixos-");
       };
 
-      # Test sudo
-      subtest "sudo", sub {
-          $machine->succeed("su - sybil -c 'sudo true'");
-      };
-
       # Test sysctl
       subtest "sysctl", sub {
           $machine->waitForUnit("systemd-sysctl.service");
diff --git a/nixos/tests/mutable-users.nix b/nixos/tests/mutable-users.nix
new file mode 100644
index 000000000000..4f11a4b83669
--- /dev/null
+++ b/nixos/tests/mutable-users.nix
@@ -0,0 +1,39 @@
+# Mutable users tests.
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "mutable-users";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ gleber ];
+  };
+
+  nodes = {
+    machine = { config, lib, pkgs, ... }: {
+      users.mutableUsers = false;
+    };
+    mutable = { config, lib, pkgs, ... }: {
+      users.mutableUsers = true;
+    };
+  };
+
+  testScript = {nodes, ...}: let
+    immutableSystem = nodes.machine.config.system.build.toplevel;
+    mutableSystem = nodes.mutable.config.system.build.toplevel;
+  in ''
+    $machine->start();
+    $machine->waitForUnit("default.target");
+
+    # Machine starts in immutable mode. Add a user and test if reactivating
+    # configuration removes the user.
+    $machine->fail("cat /etc/passwd | grep ^foobar:");
+    $machine->succeed("sudo useradd foobar");
+    $machine->succeed("cat /etc/passwd | grep ^foobar:");
+    $machine->succeed("${immutableSystem}/bin/switch-to-configuration test");
+    $machine->fail("cat /etc/passwd | grep ^foobar:");
+
+    # In immutable mode passwd is not wrapped, while in mutable mode it is
+    # wrapped.
+    $machine->succeed('which passwd | grep /run/current-system/');
+    $machine->succeed("${mutableSystem}/bin/switch-to-configuration test");
+    $machine->succeed('which passwd | grep /run/wrappers/');
+  '';
+})
diff --git a/nixos/tests/nat.nix b/nixos/tests/nat.nix
index a12b7645bc28..7057158a829b 100644
--- a/nixos/tests/nat.nix
+++ b/nixos/tests/nat.nix
@@ -35,7 +35,7 @@ import ./make-test.nix ({ pkgs, lib, withFirewall, withConntrackHelpers ? false,
             { virtualisation.vlans = [ 1 ];
               networking.firewall.allowPing = true;
               networking.defaultGateway =
-                (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ip4).address;
+                (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address;
             }
             (lib.optionalAttrs withConntrackHelpers {
               networking.firewall.connectionTrackingModules = [ "ftp" ];
diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix
new file mode 100644
index 000000000000..58733c1b3379
--- /dev/null
+++ b/nixos/tests/netdata.nix
@@ -0,0 +1,31 @@
+# This test runs netdata and checks for data via apps.plugin
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "netdata";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ cransom ];
+  };
+
+  nodes = {
+    netdata =
+      { config, pkgs, ... }:
+        {
+          environment.systemPackages = with pkgs; [ curl jq ];
+          services.netdata.enable = true;
+        };
+    };
+
+  testScript = ''
+    startAll;
+
+    $netdata->waitForUnit("netdata.service");
+    # check if netdata can read disk ops for root owned processes. 
+    # if > 0, successful. verifies both netdata working and 
+    # apps.plugin has elevated capabilities.
+    my $cmd = <<'CMD';
+    curl -s http://localhost:19999/api/v1/data\?chart=users.pwrites | \
+       jq -e '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0'
+    CMD
+    $netdata->waitUntilSucceeds($cmd);
+  '';
+})
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 7708775f73f3..5cb40af5799e 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -21,10 +21,8 @@ let
         firewall.allowedUDPPorts = [ 547 ];
         interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n:
           nameValuePair "eth${toString n}" {
-            ipAddress = "192.168.${toString n}.1";
-            prefixLength = 24;
-            ipv6Address = "fd00:1234:5678:${toString n}::1";
-            ipv6PrefixLength = 64;
+            ipv4.addresses = [ { address = "192.168.${toString n}.1"; prefixLength = 24; } ];
+            ipv6.addresses = [ { address = "fd00:1234:5678:${toString n}::1"; prefixLength = 64; } ];
           })));
       };
       services.dhcpd4 = {
@@ -90,12 +88,12 @@ let
           firewall.allowPing = true;
           useDHCP = false;
           defaultGateway = "192.168.1.1";
-          interfaces.eth1.ip4 = mkOverride 0 [
+          interfaces.eth1.ipv4.addresses = mkOverride 0 [
             { address = "192.168.1.2"; prefixLength = 24; }
             { address = "192.168.1.3"; prefixLength = 32; }
             { address = "192.168.1.10"; prefixLength = 32; }
           ];
-          interfaces.eth2.ip4 = mkOverride 0 [
+          interfaces.eth2.ipv4.addresses = mkOverride 0 [
             { address = "192.168.2.2"; prefixLength = 24; }
           ];
         };
@@ -143,12 +141,12 @@ let
           firewall.allowPing = true;
           useDHCP = true;
           interfaces.eth1 = {
-            ip4 = mkOverride 0 [ ];
-            ip6 = mkOverride 0 [ ];
+            ipv4.addresses = mkOverride 0 [ ];
+            ipv6.addresses = mkOverride 0 [ ];
           };
           interfaces.eth2 = {
-            ip4 = mkOverride 0 [ ];
-            ip6 = mkOverride 0 [ ];
+            ipv4.addresses = mkOverride 0 [ ];
+            ipv6.addresses = mkOverride 0 [ ];
           };
         };
       };
@@ -198,10 +196,10 @@ let
           firewall.allowPing = true;
           useDHCP = false;
           interfaces.eth1 = {
-            ip4 = mkOverride 0 [ ];
+            ipv4.addresses = mkOverride 0 [ ];
             useDHCP = true;
           };
-          interfaces.eth2.ip4 = mkOverride 0 [ ];
+          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
         };
       };
       testScript = { nodes, ... }:
@@ -241,9 +239,9 @@ let
             interfaces = [ "eth1" "eth2" ];
             driverOptions.mode = "balance-rr";
           };
-          interfaces.eth1.ip4 = mkOverride 0 [ ];
-          interfaces.eth2.ip4 = mkOverride 0 [ ];
-          interfaces.bond.ip4 = mkOverride 0
+          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
+          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
+          interfaces.bond.ipv4.addresses = mkOverride 0
             [ { inherit address; prefixLength = 30; } ];
         };
       };
@@ -274,7 +272,7 @@ let
           useNetworkd = networkd;
           firewall.allowPing = true;
           useDHCP = false;
-          interfaces.eth1.ip4 = mkOverride 0
+          interfaces.eth1.ipv4.addresses = mkOverride 0
             [ { inherit address; prefixLength = 24; } ];
         };
       };
@@ -289,9 +287,9 @@ let
           firewall.allowPing = true;
           useDHCP = false;
           bridges.bridge.interfaces = [ "eth1" "eth2" ];
-          interfaces.eth1.ip4 = mkOverride 0 [ ];
-          interfaces.eth2.ip4 = mkOverride 0 [ ];
-          interfaces.bridge.ip4 = mkOverride 0
+          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
+          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
+          interfaces.bridge.ipv4.addresses = mkOverride 0
             [ { address = "192.168.1.1"; prefixLength = 24; } ];
         };
       };
@@ -328,7 +326,7 @@ let
           firewall.allowPing = true;
           useDHCP = true;
           macvlans.macvlan.interface = "eth1";
-          interfaces.eth1.ip4 = mkOverride 0 [ ];
+          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
         };
       };
       testScript = { nodes, ... }:
@@ -369,9 +367,9 @@ let
             local = address4;
             dev = "eth1";
           };
-          interfaces.eth1.ip4 = mkOverride 0
+          interfaces.eth1.ipv4.addresses = mkOverride 0
             [ { address = address4; prefixLength = 24; } ];
-          interfaces.sit.ip6 = mkOverride 0
+          interfaces.sit.ipv6.addresses = mkOverride 0
             [ { address = address6; prefixLength = 64; } ];
         };
       };
@@ -410,9 +408,9 @@ let
             id = 1;
             interface = "eth0";
           };
-          interfaces.eth0.ip4 = mkOverride 0 [ ];
-          interfaces.eth1.ip4 = mkOverride 0 [ ];
-          interfaces.vlan.ip4 = mkOverride 0
+          interfaces.eth0.ipv4.addresses = mkOverride 0 [ ];
+          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
+          interfaces.vlan.ipv4.addresses = mkOverride 0
             [ { inherit address; prefixLength = 24; } ];
         };
       };
@@ -433,6 +431,169 @@ let
           $client2->succeed("ip addr show dev vlan >&2");
         '';
     };
+    virtual = {
+      name = "Virtual";
+      machine = {
+        networking.interfaces."tap0" = {
+          ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
+          ipv6.addresses = [ { address = "2001:1470:fffd:2096::"; prefixLength = 64; } ];
+          virtual = true;
+        };
+        networking.interfaces."tun0" = {
+          ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
+          ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
+          virtual = true;
+        };
+      };
+
+      testScript = ''
+        my $targetList = <<'END';
+        tap0: tap UNKNOWN_FLAGS:800 user 0
+        tun0: tun UNKNOWN_FLAGS:800 user 0
+        END
+
+        # Wait for networking to come up
+        $machine->start;
+        $machine->waitForUnit("network.target");
+
+        # Test interfaces set up
+        my $list = $machine->succeed("ip tuntap list | sort");
+        "$list" eq "$targetList" or die(
+          "The list of virtual interfaces does not match the expected one:\n",
+          "Result:\n", "$list\n",
+          "Expected:\n", "$targetList\n"
+        );
+
+        # Test interfaces clean up
+        $machine->succeed("systemctl stop network-addresses-tap0");
+        $machine->succeed("systemctl stop network-addresses-tun0");
+        my $residue = $machine->succeed("ip tuntap list");
+        $residue eq "" or die(
+          "Some virtual interface has not been properly cleaned:\n",
+          "$residue\n"
+        );
+      '';
+    };
+    privacy = {
+      name = "Privacy";
+      nodes.router = { config, pkgs, ... }: {
+        virtualisation.vlans = [ 1 ];
+        boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
+        networking = {
+          useNetworkd = networkd;
+          interfaces.eth1.ipv6.addresses = singleton {
+            address = "fd00:1234:5678:1::1";
+            prefixLength = 64;
+          };
+        };
+        services.radvd = {
+          enable = true;
+          config = ''
+            interface eth1 {
+              AdvSendAdvert on;
+              AdvManagedFlag on;
+              AdvOtherConfigFlag on;
+
+              prefix fd00:1234:5678:1::/64 {
+                AdvAutonomous on;
+                AdvOnLink on;
+              };
+            };
+          '';
+        };
+      };
+      nodes.client = { config, pkgs, ... }: with pkgs.lib; {
+        virtualisation.vlans = [ 1 ];
+        networking = {
+          useNetworkd = networkd;
+          useDHCP = true;
+          interfaces.eth1 = {
+            preferTempAddress = true;
+            ipv4.addresses = mkOverride 0 [ ];
+            ipv6.addresses = mkOverride 0 [ ];
+          };
+        };
+      };
+      testScript = { nodes, ... }:
+        ''
+          startAll;
+
+          $client->waitForUnit("network.target");
+          $router->waitForUnit("network-online.target");
+
+          # Wait until we have an ip address
+          $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");
+
+          # Test vlan 1
+          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
+
+          # Test address used is temporary
+          $client->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
+        '';
+    };
+    routes = {
+      name = "routes";
+      machine = {
+        networking.useDHCP = false;
+        networking.interfaces."eth0" = {
+          ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
+          ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
+          ipv6.routes = [
+            { address = "fdfd:b3f0::"; prefixLength = 48; }
+            { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
+          ];
+          ipv4.routes = [
+            { address = "10.0.0.0"; prefixLength = 16; options = { mtu = "1500"; }; }
+            { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
+          ];
+        };
+        virtualisation.vlans = [ ];
+      };
+
+      testScript = ''
+        my $targetIPv4Table = <<'END';
+        10.0.0.0/16 scope link mtu 1500 
+        192.168.1.0/24 proto kernel scope link src 192.168.1.2 
+        192.168.2.0/24 via 192.168.1.1 
+        END
+
+        my $targetIPv6Table = <<'END';
+        2001:1470:fffd:2097::/64 proto kernel metric 256 pref medium
+        2001:1470:fffd:2098::/64 via fdfd:b3f0::1 metric 1024 pref medium
+        fdfd:b3f0::/48 metric 1024 pref medium
+        END
+
+        $machine->start;
+        $machine->waitForUnit("network.target");
+
+        # test routing tables
+        my $ipv4Table = $machine->succeed("ip -4 route list dev eth0 | head -n3");
+        my $ipv6Table = $machine->succeed("ip -6 route list dev eth0 | head -n3");
+        "$ipv4Table" eq "$targetIPv4Table" or die(
+          "The IPv4 routing table does not match the expected one:\n",
+          "Result:\n", "$ipv4Table\n",
+          "Expected:\n", "$targetIPv4Table\n"
+        );
+        "$ipv6Table" eq "$targetIPv6Table" or die(
+          "The IPv6 routing table does not match the expected one:\n",
+          "Result:\n", "$ipv6Table\n",
+          "Expected:\n", "$targetIPv6Table\n"
+        );
+
+        # test clean-up of the tables
+        $machine->succeed("systemctl stop network-addresses-eth0");
+        my $ipv4Residue = $machine->succeed("ip -4 route list dev eth0 | head -n-3");
+        my $ipv6Residue = $machine->succeed("ip -6 route list dev eth0 | head -n-3");
+        $ipv4Residue eq "" or die(
+          "The IPv4 routing table has not been properly cleaned:\n",
+          "$ipv4Residue\n"
+        );
+        $ipv6Residue eq "" or die(
+          "The IPv6 routing table has not been properly cleaned:\n",
+          "$ipv6Residue\n"
+        );
+      '';
+    };
   };
 
 in mapAttrs (const (attrs: makeTest (attrs // {
diff --git a/nixos/tests/nix-ssh-serve.nix b/nixos/tests/nix-ssh-serve.nix
new file mode 100644
index 000000000000..aa366d8612d7
--- /dev/null
+++ b/nixos/tests/nix-ssh-serve.nix
@@ -0,0 +1,39 @@
+import ./make-test.nix ({ pkgs, lib, ... }:
+let inherit (import ./ssh-keys.nix pkgs)
+      snakeOilPrivateKey snakeOilPublicKey;
+    ssh-config = builtins.toFile "ssh.conf" ''
+      UserKnownHostsFile=/dev/null
+      StrictHostKeyChecking=no
+    '';
+in
+   { name = "nix-ssh-serve";
+     meta.maintainers = [ lib.maintainers.shlevy ];
+     nodes =
+       { server.nix.sshServe =
+           { enable = true;
+             keys = [ snakeOilPublicKey ];
+             protocol = "ssh-ng";
+           };
+         server.nix.package = pkgs.nixUnstable;
+         client.nix.package = pkgs.nixUnstable;
+       };
+     testScript = ''
+       startAll;
+
+       $client->succeed("mkdir -m 700 /root/.ssh");
+       $client->copyFileFromHost("${ssh-config}", "/root/.ssh/config");
+       $client->succeed("cat ${snakeOilPrivateKey} > /root/.ssh/id_ecdsa");
+       $client->succeed("chmod 600 /root/.ssh/id_ecdsa");
+
+       $client->succeed("nix-store --add /etc/machine-id > mach-id-path");
+
+       $server->waitForUnit("sshd");
+
+       $client->fail("diff /root/other-store\$(cat mach-id-path) /etc/machine-id");
+       # Currently due to shared store this is a noop :(
+       $client->succeed("nix copy --to ssh-ng://nix-ssh\@server \$(cat mach-id-path)");
+       $client->succeed("nix-store --realise \$(cat mach-id-path) --store /root/other-store --substituters ssh-ng://nix-ssh\@server");
+       $client->succeed("diff /root/other-store\$(cat mach-id-path) /etc/machine-id");
+     '';
+   }
+)
diff --git a/nixos/tests/novacomd.nix b/nixos/tests/novacomd.nix
new file mode 100644
index 000000000000..21b86f6dae27
--- /dev/null
+++ b/nixos/tests/novacomd.nix
@@ -0,0 +1,28 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "novacomd";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ dtzWill ];
+  };
+
+  machine = { config, pkgs, ... }: {
+    services.novacomd.enable = true;
+  };
+
+  testScript = ''
+    startAll;
+
+    $machine->waitForUnit("novacomd.service");
+
+    # Check status and try connecting with novacom
+    $machine->succeed("systemctl status novacomd.service >&2");
+    $machine->succeed("novacom -l");
+
+    # Stop the daemon, double-check novacom fails if daemon isn't working
+    $machine->stopJob("novacomd");
+    $machine->fail("novacom -l");
+
+    # And back again for good measure
+    $machine->startJob("novacomd");
+    $machine->succeed("novacom -l");
+  '';
+})
diff --git a/nixos/tests/nsd.nix b/nixos/tests/nsd.nix
index 0b1082056f6f..ad4d4f822435 100644
--- a/nixos/tests/nsd.nix
+++ b/nixos/tests/nsd.nix
@@ -15,25 +15,31 @@ in import ./make-test.nix ({ pkgs, ...} : {
     clientv4 = { lib, nodes, ... }: {
       imports = [ common ];
       networking.nameservers = lib.mkForce [
-        nodes.server.config.networking.interfaces.eth1.ipAddress
+        (lib.head nodes.server.config.networking.interfaces.eth1.ipv4.addresses).address
+      ];
+      networking.interfaces.eth1.ipv4.addresses = [
+        { address = "192.168.0.2"; prefixLength = 24; }
       ];
-      networking.interfaces.eth1.ipAddress = "192.168.0.2";
-      networking.interfaces.eth1.prefixLength = 24;
     };
 
     clientv6 = { lib, nodes, ... }: {
       imports = [ common ];
       networking.nameservers = lib.mkForce [
-        nodes.server.config.networking.interfaces.eth1.ipv6Address
+        (lib.head nodes.server.config.networking.interfaces.eth1.ipv6.addresses).address
+      ];
+      networking.interfaces.eth1.ipv4.addresses = [
+        { address = "dead:beef::2"; prefixLength = 24; }
       ];
-      networking.interfaces.eth1.ipv6Address = "dead:beef::2";
     };
 
     server = { lib, ... }: {
       imports = [ common ];
-      networking.interfaces.eth1.ipAddress = "192.168.0.1";
-      networking.interfaces.eth1.prefixLength = 24;
-      networking.interfaces.eth1.ipv6Address = "dead:beef::1";
+      networking.interfaces.eth1.ipv4.addresses = [
+        { address = "192.168.0.1"; prefixLength = 24; }
+      ];
+      networking.interfaces.eth1.ipv6.addresses = [
+        { address = "dead:beef::1"; prefixLength = 64; }
+      ];
       services.nsd.enable = true;
       services.nsd.interfaces = lib.mkForce [];
       services.nsd.zones."example.com.".data = ''
diff --git a/nixos/tests/openldap.nix b/nixos/tests/openldap.nix
new file mode 100644
index 000000000000..1eaf87a8eaa8
--- /dev/null
+++ b/nixos/tests/openldap.nix
@@ -0,0 +1,35 @@
+import ./make-test.nix {
+  name = "openldap";
+
+  machine = { pkgs, ... }: {
+    services.openldap = {
+      enable = true;
+      extraConfig = ''
+        include ${pkgs.openldap}/etc/schema/core.schema
+        include ${pkgs.openldap}/etc/schema/cosine.schema
+        include ${pkgs.openldap}/etc/schema/inetorgperson.schema
+        include ${pkgs.openldap}/etc/schema/nis.schema
+        database bdb
+        suffix dc=example
+        directory /var/db/openldap
+        rootdn cn=root,dc=example
+        rootpw notapassword
+      '';
+      declarativeContents = ''
+        dn: dc=example
+        objectClass: domain
+        dc: example
+
+        dn: ou=users,dc=example
+        objectClass: organizationalUnit
+        ou: users
+      '';
+    };
+  };
+
+  testScript = ''
+    $machine->waitForUnit('openldap.service');
+    $machine->succeed('systemctl status openldap.service');
+    $machine->succeed('ldapsearch -LLL -D "cn=root,dc=example" -w notapassword -b "dc=example"');
+  '';
+}
diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix
index 291544996889..b2d254e9d9d9 100644
--- a/nixos/tests/openssh.nix
+++ b/nixos/tests/openssh.nix
@@ -1,20 +1,7 @@
 import ./make-test.nix ({ pkgs, ... }:
 
-let
-  snakeOilPrivateKey = pkgs.writeText "privkey.snakeoil" ''
-    -----BEGIN EC PRIVATE KEY-----
-    MHcCAQEEIHQf/khLvYrQ8IOika5yqtWvI0oquHlpRLTZiJy5dRJmoAoGCCqGSM49
-    AwEHoUQDQgAEKF0DYGbBwbj06tA3fd/+yP44cvmwmHBWXZCKbS+RQlAKvLXMWkpN
-    r1lwMyJZoSGgBHoUahoYjTh9/sJL7XLJtA==
-    -----END EC PRIVATE KEY-----
-  '';
-
-  snakeOilPublicKey = pkgs.lib.concatStrings [
-    "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHA"
-    "yNTYAAABBBChdA2BmwcG49OrQN33f/sj+OHL5sJhwVl2Qim0vkUJQCry1zFpKTa"
-    "9ZcDMiWaEhoAR6FGoaGI04ff7CS+1yybQ= sakeoil"
-  ];
-
+let inherit (import ./ssh-keys.nix pkgs)
+      snakeOilPrivateKey snakeOilPublicKey;
 in {
   name = "openssh";
   meta = with pkgs.stdenv.lib.maintainers; {
diff --git a/nixos/tests/owncloud.nix b/nixos/tests/owncloud.nix
new file mode 100644
index 000000000000..0dcdea40b064
--- /dev/null
+++ b/nixos/tests/owncloud.nix
@@ -0,0 +1,39 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+{
+  name = "owncloud";
+  nodes =
+    { web =
+        { config, pkgs, ... }:
+        {
+          services.postgresql.enable = true;
+          services.httpd = {
+            enable = true;
+            logPerVirtualHost = true;
+            adminAddr = "example@example.com";
+            virtualHosts = [
+              {
+                hostName = "owncloud";
+                extraSubservices =
+                  [
+                    {
+                      serviceType   = "owncloud";
+                      adminPassword = "secret";
+                      dbPassword    = "secret";
+                    }
+                  ];
+              }
+            ];
+          };
+        };
+    };
+
+  testScript = ''
+    startAll;
+
+    $web->waitForUnit("postgresql");
+    $web->waitForUnit("httpd");
+
+    $web->succeed("curl -L 127.0.0.1:80");
+  '';
+})
diff --git a/nixos/tests/plotinus.nix b/nixos/tests/plotinus.nix
new file mode 100644
index 000000000000..557d65f7960a
--- /dev/null
+++ b/nixos/tests/plotinus.nix
@@ -0,0 +1,27 @@
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "plotinus";
+  meta = {
+    maintainers = pkgs.plotinus.meta.maintainers;
+  };
+
+  machine =
+    { config, pkgs, ... }:
+
+    { imports = [ ./common/x11.nix ];
+      programs.plotinus.enable = true;
+      environment.systemPackages = [ pkgs.gnome3.gnome-calculator pkgs.xdotool ];
+    };
+
+  testScript =
+    ''
+      $machine->waitForX;
+      $machine->execute("xterm -e 'gnome-calculator' &");
+      $machine->waitForWindow(qr/Calculator/);
+      $machine->execute("xdotool key ctrl+shift+p");
+      $machine->sleep(1); # wait for the popup
+      $machine->execute("xdotool key p r e f e r e n c e s Return");
+      $machine->waitForWindow(qr/Preferences/);
+      $machine->screenshot("screen");
+    '';
+
+})
diff --git a/nixos/tests/postgis.nix b/nixos/tests/postgis.nix
index f6ce3fe38ed3..7fe905eb4254 100644
--- a/nixos/tests/postgis.nix
+++ b/nixos/tests/postgis.nix
@@ -12,7 +12,7 @@ import ./make-test.nix ({ pkgs, ...} : {
         services.postgresql = let mypg = pkgs.postgresql100; in {
             enable = true;
             package = mypg;
-            extraPlugins = [ (pkgs.postgis.override { postgresql = mypg; }).v_2_4_0 ];
+            extraPlugins = [ (pkgs.postgis.override { postgresql = mypg; }) ];
         };
       };
   };
diff --git a/nixos/tests/powerdns.nix b/nixos/tests/powerdns.nix
new file mode 100644
index 000000000000..0d5b0f715f52
--- /dev/null
+++ b/nixos/tests/powerdns.nix
@@ -0,0 +1,12 @@
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "powerdns";
+
+  nodes.server = { config, pkgs, ... }: {
+    services.powerdns.enable = true;
+  };
+
+  testScript = ''
+    $server->waitForUnit("pdns");
+    $server->succeed("${pkgs.dnsutils}/bin/dig version.bind txt chaos \@127.0.0.1");
+  '';
+})
diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix
new file mode 100644
index 000000000000..b4c2039923cf
--- /dev/null
+++ b/nixos/tests/predictable-interface-names.nix
@@ -0,0 +1,27 @@
+{ system ? builtins.currentSystem
+, pkgs ? import ../.. { inherit system; }
+}:
+with import ../lib/testing.nix { inherit system; };
+let boolToString = x: if x then "yes" else "no"; in
+let testWhenSetTo = predictable: withNetworkd:
+makeTest {
+  name = "${if predictable then "" else "un"}predictableInterfaceNames${if withNetworkd then "-with-networkd" else ""}";
+  meta = {};
+
+  machine = { config, pkgs, ... }: {
+    networking.usePredictableInterfaceNames = pkgs.stdenv.lib.mkForce predictable;
+    networking.useNetworkd = withNetworkd;
+    networking.dhcpcd.enable = !withNetworkd;
+  };
+
+  testScript = ''
+    print $machine->succeed("ip link");
+    $machine->succeed("ip link show ${if predictable then "ens3" else "eth0"}");
+    $machine->fail("ip link show ${if predictable then "eth0" else "ens3"}");
+  '';
+}; in
+with pkgs.stdenv.lib.lists;
+with pkgs.stdenv.lib.attrsets;
+listToAttrs (map (drv: nameValuePair drv.name drv) (
+crossLists testWhenSetTo [[true false] [true false]]
+))
diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix
index e44e5bf11d33..989008830613 100644
--- a/nixos/tests/printing.nix
+++ b/nixos/tests/printing.nix
@@ -39,7 +39,9 @@ import ./make-test.nix ({pkgs, ... }: {
       $client->waitForUnit("cups.service");
       $client->sleep(10); # wait until cups is fully initialized
       $client->succeed("lpstat -r") =~ /scheduler is running/ or die;
+      # Test that UNIX socket is used for connections.
       $client->succeed("lpstat -H") =~ "/var/run/cups/cups.sock" or die;
+      # Test that HTTP server is available too.
       $client->succeed("curl --fail http://localhost:631/");
       $client->succeed("curl --fail http://server:631/");
       $server->fail("curl --fail --connect-timeout 2  http://client:631/");
diff --git a/nixos/tests/prosody.nix b/nixos/tests/prosody.nix
new file mode 100644
index 000000000000..fcebfaf74e12
--- /dev/null
+++ b/nixos/tests/prosody.nix
@@ -0,0 +1,75 @@
+import ./make-test.nix {
+  name = "prosody";
+
+  machine = { config, pkgs, ... }: {
+    services.prosody = {
+      enable = true;
+      # TODO: use a self-signed certificate
+      c2sRequireEncryption = false;
+    };
+    environment.systemPackages = let
+      sendMessage = pkgs.writeScriptBin "send-message" ''
+        #!/usr/bin/env python3
+        # Based on the sleekxmpp send_client example, look there for more details:
+        # https://github.com/fritzy/SleekXMPP/blob/develop/examples/send_client.py
+        import sleekxmpp
+
+        class SendMsgBot(sleekxmpp.ClientXMPP):
+            """
+            A basic SleekXMPP bot that will log in, send a message,
+            and then log out.
+            """
+            def __init__(self, jid, password, recipient, message):
+                sleekxmpp.ClientXMPP.__init__(self, jid, password)
+
+                self.recipient = recipient
+                self.msg = message
+
+                self.add_event_handler("session_start", self.start, threaded=True)
+
+            def start(self, event):
+                self.send_presence()
+                self.get_roster()
+
+                self.send_message(mto=self.recipient,
+                                  mbody=self.msg,
+                                  mtype='chat')
+
+                self.disconnect(wait=True)
+
+
+        if __name__ == '__main__':
+            xmpp = SendMsgBot("test1@localhost", "test1", "test2@localhost", "Hello World!")
+            xmpp.register_plugin('xep_0030') # Service Discovery
+            xmpp.register_plugin('xep_0199') # XMPP Ping
+
+            # TODO: verify certificate
+            # If you want to verify the SSL certificates offered by a server:
+            # xmpp.ca_certs = "path/to/ca/cert"
+
+            if xmpp.connect(('localhost', 5222)):
+                xmpp.process(block=True)
+            else:
+                print("Unable to connect.")
+                sys.exit(1)
+      '';
+    in [ (pkgs.python3.withPackages (ps: [ ps.sleekxmpp ])) sendMessage ];
+  };
+
+  testScript = ''
+    $machine->waitForUnit('prosody.service');
+    $machine->succeed('prosodyctl status') =~ /Prosody is running/;
+
+    # set password to 'test' (it's asked twice)
+    $machine->succeed('yes test1 | prosodyctl adduser test1@localhost');
+    # set password to 'y'
+    $machine->succeed('yes | prosodyctl adduser test2@localhost');
+    # correct password to 'test2'
+    $machine->succeed('yes test2 | prosodyctl passwd test2@localhost');
+
+    $machine->succeed("send-message");
+
+    $machine->succeed('prosodyctl deluser test1@localhost');
+    $machine->succeed('prosodyctl deluser test2@localhost');
+  '';
+}
diff --git a/nixos/tests/pump.io.nix b/nixos/tests/pump.io.nix
deleted file mode 100644
index 0991ed3fce1f..000000000000
--- a/nixos/tests/pump.io.nix
+++ /dev/null
@@ -1,89 +0,0 @@
-# This test runs pump.io with mongodb, listing on port 443.
-
-import ./make-test.nix ({ pkgs, ...} : let
-  snakeOilKey = ''
-    -----BEGIN PRIVATE KEY-----
-    MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqVemio78R41Tz
-    MnR2zFD/wFT0iScOpFkuytNmuPf28FLaa9wSBWmuAGbEi7wBIfw8/bUqFBTQp2G1
-    m1cmcCKxhmvvOkGs89eM131s1lW/bXU3zYso4e7724kHwU65jRlQs6cFWIlmW7V5
-    3HQobP05dy+zPpujPPSlOQ0qYViR1s+RgZI8r0wS2ZDsliNtQwBLJSIvX6XVnXLo
-    F/HmF4/ySJ9pL2AxQXCwZE8SfCzHpArs9COIqTaAuwB79kxWSFQJewmab74BXiM6
-    9FMCtHON24Pl7OR9sRJHH8rMEzUumppmUeCNEzABjzQQ7svR18cmbzRWetp0tT9Y
-    7rj6URHHAgMBAAECggEAGmbCldDnlrAzxJY3cwpsK5f2EwkHIr/aiuQpLCzTUlUh
-    onVBYRGxtaSeSSyXcV2BKTrxz5nZOBYZkPqI4Y5T8kwxgpz2/QW2jUABUtNN6yPe
-    HU4gma+bSTJX5PnTZ/M0z0tpQezdLx5b3I2M+48ZGMUegZvcp8qU6N8U6VK5VbFD
-    DMTGL4b+Kc9HScRkCJjU3FfQcqf9Ml5w9jzHSeHImYEDrG0nX8N8EImRCBXbgxCl
-    5XT1h6LFUGdr+N6n2w56+6l8OZZVmwj1NdF6NJybUQl4Y7b0niA+5czzjRt/YUjZ
-    HW0fXmx3XlbYGWYdMdS+VaIW6pkUpm8kZkqjngqLwQKBgQDfhbFQmg9lsJQ8/dQZ
-    WzRNsozHKWkQiZbW5sXBWygJbAB3Hc8gvQkuZe9TVyF99cznRj6ro6pGZjP0rTdY
-    3ACTL+ygRArcIR6VsJCIr6nPvBLpOoNb8TQeKPmHC2gnSP9zaT/K2lldYISKNaYQ
-    0seB2gvZhIgMgWtZtmb3jdgl9wKBgQDDFdknXgvFgB+y96//9wTu2WWuE5yQ5yB7
-    utAcHNO9rx5X1tJqxymYh+iE8HUN25By+96SpNMQFI+0wNGVB00YWNBKtyepimWN
-    EUCojTy+MIXIjrLcvviEePsI4TPWYf8XtZeiYtcczYrt/wPQUYaDb8LBRfpIfmhr
-    rCGW93s+sQKBgEDOKTeeQyKPjJsWWL01RTfVsZ04s155FcOeyu0heb0plAT1Ho12
-    YUgTg8zc8Tfs4QiYxCjNXdvlW+Dvq6FWv8/s0CUzNRbXf1+U/oKys4AoHi+CqH0q
-    tJqd9KKjuwHQ10dl13n/znMVPbg4j7pG8lMCnfblxvAhQbeT+8yAUo/HAoGBAL3t
-    /n4KXNGK3NHDvXEp0H6t3wWsiEi3DPQJO+Wy1x8caCFCv5c/kaqz3tfWt0+njSm1
-    N8tzdx13tzVWaHV8Jz3l8dxcFtxEJnxB6L5wy0urOAS7kT3DG3b1xgmuH2a//7fY
-    jumE60NahcER/2eIh7pdS7IZbAO6NfVmH0m4Zh/xAoGAbquh60sAfLC/1O2/4Xom
-    PHS7z2+TNpwu4ou3nspxfigNQcTWzzzTVFLnaTPg+HKbLRXSWysjssmmj5u3lCyc
-    S2M9xuhApa9CrN/udz4gEojRVsTla/gyLifIZ3CtTn2QEQiIJEMxM+59KAlkgUBo
-    9BeZ03xTaEZfhVZ9bEN30Ak=
-    -----END PRIVATE KEY-----
-  '';
-
-  snakeOilCert = ''
-    -----BEGIN CERTIFICATE-----
-    MIICvjCCAaagAwIBAgIJANhA6+PPhomZMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV
-    BAMMDGIwOTM0YWMwYWZkNTAeFw0xNTExMzAxNzQ3MzVaFw0yNTExMjcxNzQ3MzVa
-    MBcxFTATBgNVBAMMDGIwOTM0YWMwYWZkNTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-    ADCCAQoCggEBAKpV6aKjvxHjVPMydHbMUP/AVPSJJw6kWS7K02a49/bwUtpr3BIF
-    aa4AZsSLvAEh/Dz9tSoUFNCnYbWbVyZwIrGGa+86Qazz14zXfWzWVb9tdTfNiyjh
-    7vvbiQfBTrmNGVCzpwVYiWZbtXncdChs/Tl3L7M+m6M89KU5DSphWJHWz5GBkjyv
-    TBLZkOyWI21DAEslIi9fpdWdcugX8eYXj/JIn2kvYDFBcLBkTxJ8LMekCuz0I4ip
-    NoC7AHv2TFZIVAl7CZpvvgFeIzr0UwK0c43bg+Xs5H2xEkcfyswTNS6ammZR4I0T
-    MAGPNBDuy9HXxyZvNFZ62nS1P1juuPpREccCAwEAAaMNMAswCQYDVR0TBAIwADAN
-    BgkqhkiG9w0BAQsFAAOCAQEAd2w9rxi6qF9WV8L3rHnTE7uu0ldtdgJlCASx6ouj
-    TleOnjfEg+kH8r8UbmRV5vsTDn1Qp5JGDYxfytRUQwLb1zTLde0xotx37E3LY8Wr
-    sD6Al4t8sHywB/hc5dy29TgG0iyG8LKZrkwytLvDZ814W3OwpN2rpEz6pdizdHNn
-    jsoDEngZiDHvLjIyE0cDkFXkeYMGXOnBUeOcu4nfu4C5eKs3nXGGAcNDbDRIuLoE
-    BZExUBY+YSs6JBvh5tvRqLVW0Dz0akEcjb/jhwS2LmDip8Pdoxx4Q1jPKEu38zrr
-    Vd5WD2HJhLb9u0UxVp9vfWIUDgydopV5ZmWCQ5YvNepb1w==
-    -----END CERTIFICATE-----
-  '';
-in {
-  name = "pumpio";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ rvl ];
-  };
-
-  nodes = {
-    one =
-      { config, pkgs, ... }:
-        {
-          services = {
-           pumpio = {
-             port = 443;
-             enable = true;
-             sslCert = pkgs.writeText "snakeoil.cert" snakeOilCert;
-             sslKey = pkgs.writeText "snakeoil.pem" snakeOilKey;
-             secretFile = pkgs.writeText "secretFile" "test123";
-             site = "test";
-           };
-           mongodb.enable = true;
-           mongodb.extraConfig = ''
-             storage.journal.enabled: false
-           '';
-          };
-          systemd.services.mongodb.unitConfig.Before = "pump.io.service";
-          systemd.services."pump.io".unitConfig.Requires = "mongodb.service";
-        };
-    };
-
-  testScript = ''
-    startAll;
-
-    $one->waitForUnit("pump.io.service");
-    $one->waitUntilSucceeds("curl -k https://localhost");
-  '';
-})
diff --git a/nixos/tests/quagga.nix b/nixos/tests/quagga.nix
index b9644b4768c0..613180942c41 100644
--- a/nixos/tests/quagga.nix
+++ b/nixos/tests/quagga.nix
@@ -8,7 +8,7 @@
 import ./make-test.nix ({ pkgs, ... }:
   let
 
-    ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ip4).address;
+    ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ipv4.addresses).address;
 
     ospfConf = ''
       interface eth2
diff --git a/nixos/tests/radicale.nix b/nixos/tests/radicale.nix
index f694fc75ef77..8ac0639c6a8c 100644
--- a/nixos/tests/radicale.nix
+++ b/nixos/tests/radicale.nix
@@ -20,7 +20,7 @@ let
       '';
     };
     # WARNING: DON'T DO THIS IN PRODUCTION!
-    # This puts secrets (albeit hashed) directly into the Nix store for ease of testing.
+    # This puts unhashed secrets directly into the Nix store for ease of testing.
     environment.etc."radicale/htpasswd".source = pkgs.runCommand "htpasswd" {} ''
       ${pkgs.apacheHttpd}/bin/htpasswd -bcB "$out" ${user} ${password}
     '';
diff --git a/nixos/tests/rspamd.nix b/nixos/tests/rspamd.nix
new file mode 100644
index 000000000000..6b2e2dd3a531
--- /dev/null
+++ b/nixos/tests/rspamd.nix
@@ -0,0 +1,140 @@
+{ system ? builtins.currentSystem }:
+with import ../lib/testing.nix { inherit system; };
+with pkgs.lib;
+let
+  initMachine = ''
+    startAll
+    $machine->waitForUnit("rspamd.service");
+    $machine->succeed("id \"rspamd\" >/dev/null");
+  '';
+  checkSocket = socket: user: group: mode: ''
+    $machine->succeed("ls ${socket} >/dev/null");
+    $machine->succeed("[[ \"\$(stat -c %U ${socket})\" == \"${user}\" ]]");
+    $machine->succeed("[[ \"\$(stat -c %G ${socket})\" == \"${group}\" ]]");
+    $machine->succeed("[[ \"\$(stat -c %a ${socket})\" == \"${mode}\" ]]");
+  '';
+  simple = name: socketActivation: enableIPv6: makeTest {
+    name = "rspamd-${name}";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        socketActivation = socketActivation;
+      };
+      networking.enableIPv6 = enableIPv6;
+    };
+    testScript = ''
+      startAll
+      $machine->waitForUnit("multi-user.target");
+      $machine->waitForOpenPort(11334);
+      $machine->waitForUnit("rspamd.service");
+      $machine->succeed("id \"rspamd\" >/dev/null");
+      ${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" }
+      sleep 10;
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->log($machine->succeed("systemctl cat rspamd.service"));
+      ${if socketActivation then ''
+        $machine->log($machine->succeed("systemctl cat rspamd-controller-1.socket"));
+        $machine->log($machine->succeed("systemctl cat rspamd-normal-1.socket"));
+      '' else ''
+        $machine->fail("systemctl cat rspamd-controller-1.socket");
+        $machine->fail("systemctl cat rspamd-normal-1.socket");
+      ''}
+      $machine->log($machine->succeed("curl http://localhost:11334/auth"));
+      $machine->log($machine->succeed("curl http://127.0.0.1:11334/auth"));
+      ${optionalString enableIPv6 ''
+        $machine->log($machine->succeed("curl http://[::1]:11334/auth"));
+      ''}
+    '';
+  };
+in
+{
+  simple = simple "simple" false true;
+  ipv4only = simple "ipv4only" false false;
+  simple-socketActivated = simple "simple-socketActivated" true true;
+  ipv4only-socketActivated = simple "ipv4only-socketActivated" true false;
+  deprecated = makeTest {
+    name = "rspamd-deprecated";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        bindSocket = [ "/run/rspamd.sock mode=0600 user=root group=root" ];
+        bindUISocket = [ "/run/rspamd-worker.sock mode=0666 user=root group=root" ];
+      };
+    };
+
+    testScript = ''
+      ${initMachine}
+      $machine->waitForFile("/run/rspamd.sock");
+      ${checkSocket "/run/rspamd.sock" "root" "root" "600" }
+      ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->fail("systemctl cat rspamd-normal-1.socket");
+      $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
+      $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
+    '';
+  };
+
+  bindports = makeTest {
+    name = "rspamd-bindports";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        socketActivation = false;
+        workers.normal.bindSockets = [{
+          socket = "/run/rspamd.sock";
+          mode = "0600";
+          owner = "root";
+          group = "root";
+        }];
+        workers.controller.bindSockets = [{
+          socket = "/run/rspamd-worker.sock";
+          mode = "0666";
+          owner = "root";
+          group = "root";
+        }];
+      };
+    };
+
+    testScript = ''
+      ${initMachine}
+      $machine->waitForFile("/run/rspamd.sock");
+      ${checkSocket "/run/rspamd.sock" "root" "root" "600" }
+      ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->fail("systemctl cat rspamd-normal-1.socket");
+      $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
+      $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
+    '';
+  };
+  socketActivated = makeTest {
+    name = "rspamd-socketActivated";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        workers.normal.bindSockets = [{
+          socket = "/run/rspamd.sock";
+          mode = "0600";
+          owner = "root";
+          group = "root";
+        }];
+        workers.controller.bindSockets = [{
+          socket = "/run/rspamd-worker.sock";
+          mode = "0666";
+          owner = "root";
+          group = "root";
+        }];
+      };
+    };
+
+    testScript = ''
+      startAll
+      $machine->waitForFile("/run/rspamd.sock");
+      ${checkSocket "/run/rspamd.sock" "root" "root" "600" }
+      ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->log($machine->succeed("systemctl cat rspamd-normal-1.socket"));
+      $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
+      $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
+    '';
+  };
+}
diff --git a/nixos/tests/rxe.nix b/nixos/tests/rxe.nix
new file mode 100644
index 000000000000..cfe64a75a635
--- /dev/null
+++ b/nixos/tests/rxe.nix
@@ -0,0 +1,53 @@
+import ./make-test.nix ({ pkgs, ... } :
+
+let
+  node = { config, pkgs, lib, ... } : {
+    networking = {
+      firewall = {
+        allowedUDPPorts = [ 4791 ]; # open RoCE port
+        allowedTCPPorts = [ 4800 ]; # port for test utils
+      };
+      rxe = {
+        enable = true;
+        interfaces = [ "eth1" ];
+      };
+    };
+
+    environment.systemPackages = with pkgs; [ rdma-core screen ];
+  };
+
+in {
+  name = "rxe";
+
+  nodes = {
+    server = node;
+    client = node;
+  };
+
+  testScript = ''
+    # Test if rxe interface comes up
+    $server->waitForUnit("default.target");
+    $server->succeed("systemctl status rxe.service");
+    $server->succeed("ibv_devices | grep rxe0");
+
+    $client->waitForUnit("default.target");
+
+    # ping pong test
+    $server->succeed("screen -dmS rc_pingpong ibv_rc_pingpong -p 4800 -g0");
+    $client->succeed("sleep 2; ibv_rc_pingpong -p 4800 -g0 server");
+
+    $server->succeed("screen -dmS uc_pingpong ibv_uc_pingpong -p 4800 -g0");
+    $client->succeed("sleep 2; ibv_uc_pingpong -p 4800 -g0 server");
+
+    $server->succeed("screen -dmS ud_pingpong ibv_ud_pingpong -p 4800 -s 1024 -g0");
+    $client->succeed("sleep 2; ibv_ud_pingpong -p 4800 -s 1024 -g0 server");
+
+    $server->succeed("screen -dmS srq_pingpong ibv_srq_pingpong -p 4800 -g0");
+    $client->succeed("sleep 2; ibv_srq_pingpong -p 4800 -g0 server");
+
+    $server->succeed("screen -dmS rping rping -s -a server -C 10");
+    $client->succeed("sleep 2; rping -c -a server -C 10");
+  '';
+})
+
+
diff --git a/nixos/tests/ssh-keys.nix b/nixos/tests/ssh-keys.nix
new file mode 100644
index 000000000000..07d422196efa
--- /dev/null
+++ b/nixos/tests/ssh-keys.nix
@@ -0,0 +1,15 @@
+pkgs:
+{ snakeOilPrivateKey = pkgs.writeText "privkey.snakeoil" ''
+    -----BEGIN EC PRIVATE KEY-----
+    MHcCAQEEIHQf/khLvYrQ8IOika5yqtWvI0oquHlpRLTZiJy5dRJmoAoGCCqGSM49
+    AwEHoUQDQgAEKF0DYGbBwbj06tA3fd/+yP44cvmwmHBWXZCKbS+RQlAKvLXMWkpN
+    r1lwMyJZoSGgBHoUahoYjTh9/sJL7XLJtA==
+    -----END EC PRIVATE KEY-----
+  '';
+
+  snakeOilPublicKey = pkgs.lib.concatStrings [
+    "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHA"
+    "yNTYAAABBBChdA2BmwcG49OrQN33f/sj+OHL5sJhwVl2Qim0vkUJQCry1zFpKTa"
+    "9ZcDMiWaEhoAR6FGoaGI04ff7CS+1yybQ= sakeoil"
+  ];
+}
diff --git a/nixos/tests/statsd.nix b/nixos/tests/statsd.nix
new file mode 100644
index 000000000000..a9d7dc61cb60
--- /dev/null
+++ b/nixos/tests/statsd.nix
@@ -0,0 +1,40 @@
+import ./make-test.nix ({ pkgs, lib, ... }:
+
+with lib;
+
+{
+  name = "statsd";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ ma27 ];
+  };
+
+  nodes.statsd1 = {
+    services.statsd.enable = true;
+    services.statsd.backends = [ "statsd-influxdb-backend" "console" ];
+    services.statsd.extraConfig = ''
+      influxdb: {
+        username: "root",
+        password: "root",
+        database: "statsd"
+      }
+    '';
+
+    services.influxdb.enable = true;
+
+    systemd.services.influx-init = {
+      description = "Setup Influx Test Base";
+      after = [ "influxdb.service" ];
+      before = [ "statsd.service" ];
+
+      script = ''
+        echo "CREATE DATABASE statsd" | ${pkgs.influxdb}/bin/influx
+      '';
+    };
+  };
+
+  testScript = ''
+    $statsd1->start();
+    $statsd1->waitForUnit("statsd.service");
+    $statsd1->succeed("nc -z 127.0.0.1 8126");
+  '';
+})
diff --git a/nixos/tests/sudo.nix b/nixos/tests/sudo.nix
new file mode 100644
index 000000000000..35addb0ee805
--- /dev/null
+++ b/nixos/tests/sudo.nix
@@ -0,0 +1,93 @@
+# Some tests to ensure sudo is working properly.
+
+let
+  password = "helloworld";
+
+in
+  import ./make-test.nix ({ pkgs, ...} : {
+    name = "sudo";
+    meta = with pkgs.stdenv.lib.maintainers; {
+      maintainers = [ lschuermann ];
+    };
+
+    machine =
+      { config, lib, pkgs, ... }:
+      with lib;
+      {
+        users.extraGroups = { foobar = {}; barfoo = {}; baz = { gid = 1337; }; };
+        users.users = {
+          test0 = { isNormalUser = true; extraGroups = [ "wheel" ]; };
+          test1 = { isNormalUser = true; password = password; };
+          test2 = { isNormalUser = true; extraGroups = [ "foobar" ]; password = password; };
+          test3 = { isNormalUser = true; extraGroups = [ "barfoo" ]; };
+          test4 = { isNormalUser = true; extraGroups = [ "baz" ]; };
+          test5 = { isNormalUser = true; };
+        };
+
+        security.sudo = {
+          enable = true;
+          wheelNeedsPassword = false;
+
+          extraRules = [
+            # SUDOERS SYNTAX CHECK (Test whether the module produces a valid output;
+            # errors being detected by the visudo checks.
+
+            # These should not create any entries
+            { users = [ "notest1" ]; commands = [ ]; }
+            { commands = [ { command = "ALL"; options = [ ]; } ]; }
+
+            # Test defining commands with the options syntax, though not setting any options
+            { users = [ "notest2" ]; commands = [ { command = "ALL"; options = [ ]; } ]; }
+
+
+            # CONFIGURATION FOR TEST CASES
+            { users = [ "test1" ]; groups = [ "foobar" ]; commands = [ "ALL" ]; }
+            { groups = [ "barfoo" 1337 ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "NOSETENV" ]; } ]; }
+            { users = [ "test5" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "SETENV" ]; } ]; runAs = "test1:barfoo"; }
+          ];
+        };
+      };
+
+    testScript =
+      ''
+        subtest "users in wheel group should have passwordless sudo", sub {
+            $machine->succeed("su - test0 -c \"sudo -u root true\"");
+        };
+
+        subtest "test1 user should have sudo with password", sub {
+            $machine->succeed("su - test1 -c \"echo ${password} | sudo -S -u root true\"");
+        };
+
+        subtest "test1 user should not be able to use sudo without password", sub {
+            $machine->fail("su - test1 -c \"sudo -n -u root true\"");
+        };
+
+        subtest "users in group 'foobar' should be able to use sudo with password", sub {
+            $machine->succeed("sudo -u test2 echo ${password} | sudo -S -u root true");
+        };
+
+        subtest "users in group 'barfoo' should be able to use sudo without password", sub {
+            $machine->succeed("sudo -u test3 sudo -n -u root true");
+        };
+
+        subtest "users in group 'baz' (GID 1337) should be able to use sudo without password", sub {
+            $machine->succeed("sudo -u test4 sudo -n -u root echo true");
+        };
+
+        subtest "test5 user should be able to run commands under test1", sub {
+            $machine->succeed("sudo -u test5 sudo -n -u test1 true");
+        };
+
+        subtest "test5 user should not be able to run commands under root", sub {
+            $machine->fail("sudo -u test5 sudo -n -u root true");
+        };
+
+        subtest "test5 user should be able to keep his environment", sub {
+            $machine->succeed("sudo -u test5 sudo -n -E -u test1 true");
+        };
+
+        subtest "users in group 'barfoo' should not be able to keep their environment", sub {
+            $machine->fail("sudo -u test3 sudo -n -E -u root true");
+        };
+      '';
+  })
diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix
new file mode 100644
index 000000000000..46f2563af8d9
--- /dev/null
+++ b/nixos/tests/switch-test.nix
@@ -0,0 +1,25 @@
+# Test configuration switching.
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "switch-test";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ gleber ];
+  };
+
+  nodes = {
+    machine = { config, lib, pkgs, ... }: {
+      users.mutableUsers = false;
+    };
+    other = { config, lib, pkgs, ... }: {
+      users.mutableUsers = true;
+    };
+  };
+
+  testScript = {nodes, ...}: let
+    originalSystem = nodes.machine.config.system.build.toplevel;
+    otherSystem = nodes.other.config.system.build.toplevel;
+  in ''
+    $machine->succeed("env -i ${originalSystem}/bin/switch-to-configuration test | tee /dev/stderr");
+    $machine->succeed("env -i ${otherSystem}/bin/switch-to-configuration test | tee /dev/stderr");
+  '';
+})
diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix
new file mode 100644
index 000000000000..65aa553b3148
--- /dev/null
+++ b/nixos/tests/systemd.nix
@@ -0,0 +1,68 @@
+import ./make-test.nix {
+  name = "systemd";
+
+  machine = { lib, ... }: {
+    imports = [ common/user-account.nix common/x11.nix ];
+
+    virtualisation.emptyDiskImages = [ 512 ];
+
+    fileSystems = lib.mkVMOverride {
+      "/test-x-initrd-mount" = {
+        device = "/dev/vdb";
+        fsType = "ext2";
+        autoFormat = true;
+        noCheck = true;
+        options = [ "x-initrd.mount" ];
+      };
+    };
+
+    systemd.extraConfig = "DefaultEnvironment=\"XXX_SYSTEM=foo\"";
+    systemd.user.extraConfig = "DefaultEnvironment=\"XXX_USER=bar\"";
+    services.journald.extraConfig = "Storage=volatile";
+    services.xserver.displayManager.auto.user = "alice";
+
+    systemd.services.testservice1 = {
+      description = "Test Service 1";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.Type = "oneshot";
+      script = ''
+        if [ "$XXX_SYSTEM" = foo ]; then
+          touch /system_conf_read
+        fi
+      '';
+    };
+
+    systemd.user.services.testservice2 = {
+      description = "Test Service 2";
+      wantedBy = [ "default.target" ];
+      serviceConfig.Type = "oneshot";
+      script = ''
+        if [ "$XXX_USER" = bar ]; then
+          touch "$HOME/user_conf_read"
+        fi
+      '';
+    };
+  };
+
+  testScript = ''
+    $machine->waitForX;
+    # wait for user services
+    $machine->waitForUnit("default.target","alice");
+
+    # Regression test for https://github.com/NixOS/nixpkgs/issues/35415
+    subtest "configuration files are recognized by systemd", sub {
+      $machine->succeed('test -e /system_conf_read');
+      $machine->succeed('test -e /home/alice/user_conf_read');
+      $machine->succeed('test -z $(ls -1 /var/log/journal)');
+    };
+
+    # Regression test for https://github.com/NixOS/nixpkgs/issues/35268
+    subtest "file system with x-initrd.mount is not unmounted", sub {
+      $machine->shutdown;
+      $machine->waitForUnit('multi-user.target');
+      # If the file system was unmounted during the shutdown the file system
+      # has a last mount time, because the file system wasn't checked.
+      $machine->fail('dumpe2fs /dev/vdb | grep -q "^Last mount time: *n/a"');
+    };
+  '';
+}
diff --git a/nixos/tests/transmission.nix b/nixos/tests/transmission.nix
new file mode 100644
index 000000000000..34c49bd7f15b
--- /dev/null
+++ b/nixos/tests/transmission.nix
@@ -0,0 +1,21 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "transmission";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ coconnor ];
+  };
+
+  machine = { config, pkgs, ... }: {
+    imports = [ ../modules/profiles/minimal.nix ];
+
+    networking.firewall.allowedTCPPorts = [ 9091 ];
+
+    services.transmission.enable = true;
+  };
+
+  testScript =
+    ''
+      startAll;
+      $machine->waitForUnit("transmission");
+      $machine->shutdown;
+    '';
+})
diff --git a/nixos/tests/vault.nix b/nixos/tests/vault.nix
new file mode 100644
index 000000000000..515d5c8bac25
--- /dev/null
+++ b/nixos/tests/vault.nix
@@ -0,0 +1,23 @@
+import ./make-test.nix ({ pkgs, ... }:
+{
+  name = "vault";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lnl7 ];
+  };
+  machine = { config, pkgs, ... }: {
+    environment.systemPackages = [ pkgs.vault ];
+    environment.variables.VAULT_ADDR = "http://127.0.0.1:8200";
+    services.vault.enable = true;
+  };
+
+  testScript =
+    ''
+      startAll;
+
+      $machine->waitForUnit('multi-user.target');
+      $machine->waitForUnit('vault.service');
+      $machine->waitForOpenPort(8200);
+      $machine->succeed('vault operator init');
+      $machine->succeed('vault status | grep Sealed | grep true');
+    '';
+})
diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix
index c519d7dae8be..249571fcedec 100644
--- a/nixos/tests/virtualbox.nix
+++ b/nixos/tests/virtualbox.nix
@@ -43,6 +43,9 @@ let
       "init=${pkgs.writeScript "mini-init.sh" miniInit}"
     ];
 
+    # XXX: Remove this once TSS location detection has been fixed in VirtualBox
+    boot.kernelPackages = pkgs.linuxPackages_4_9;
+
     fileSystems."/" = {
       device = "vboxshare";
       fsType = "vboxsf";
@@ -109,9 +112,6 @@ let
   } ''
     ${pkgs.parted}/sbin/parted --script /dev/vda mklabel msdos
     ${pkgs.parted}/sbin/parted --script /dev/vda -- mkpart primary ext2 1M -1s
-    . /sys/class/block/vda1/uevent
-    mknod /dev/vda1 b $MAJOR $MINOR
-
     ${pkgs.e2fsprogs}/sbin/mkfs.ext4 /dev/vda1
     ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
     mkdir /mnt
diff --git a/nixos/tests/xautolock.nix b/nixos/tests/xautolock.nix
new file mode 100644
index 000000000000..ee46d9e05b06
--- /dev/null
+++ b/nixos/tests/xautolock.nix
@@ -0,0 +1,24 @@
+import ./make-test.nix ({ pkgs, lib, ... }:
+
+with lib;
+
+{
+  name = "xautolock";
+  meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ];
+
+  nodes.machine = {
+    imports = [ ./common/x11.nix ./common/user-account.nix ];
+
+    services.xserver.displayManager.auto.user = "bob";
+    services.xserver.xautolock.enable = true;
+    services.xserver.xautolock.time = 1;
+  };
+
+  testScript = ''
+    $machine->start;
+    $machine->waitForX;
+    $machine->mustFail("pgrep xlock");
+    $machine->sleep(120);
+    $machine->mustSucceed("pgrep xlock");
+  '';
+})
diff --git a/nixos/tests/yabar.nix b/nixos/tests/yabar.nix
new file mode 100644
index 000000000000..40ca91e8064d
--- /dev/null
+++ b/nixos/tests/yabar.nix
@@ -0,0 +1,25 @@
+import ./make-test.nix ({ pkgs, lib }:
+
+with lib;
+
+{
+  name = "yabar";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ ma27 ];
+  };
+
+  nodes.yabar = {
+    imports = [ ./common/x11.nix ./common/user-account.nix ];
+
+    services.xserver.displayManager.auto.user = "bob";
+
+    programs.yabar.enable = true;
+  };
+
+  testScript = ''
+    $yabar->start;
+    $yabar->waitForX;
+
+    $yabar->waitForUnit("yabar.service", "bob");
+  '';
+})